Browse Source

Initial commit

Yann Weber 4 years ago
commit
a42b237918
27 changed files with 4037 additions and 0 deletions
  1. 258
    0
      Doxyfile.mk
  2. 9
    0
      LICENCE.txt
  3. 91
    0
      Makefile
  4. 45
    0
      README
  5. 1
    0
      VERSION
  6. 55
    0
      check_deps.sh
  7. 22
    0
      config.h
  8. 129
    0
      python_pyrpn.c
  9. 96
    0
      python_pyrpn.h
  10. 283
    0
      python_rpnexpr.c
  11. 120
    0
      python_rpnexpr.h
  12. 145
    0
      rpn_if.c
  13. 220
    0
      rpn_if.h
  14. 41
    0
      rpn_ifs.h
  15. 465
    0
      rpn_jit.c
  16. 251
    0
      rpn_jit.h
  17. 246
    0
      rpn_lib.asm
  18. 122
    0
      rpn_lib.h
  19. 58
    0
      rpn_mutation.c
  20. 83
    0
      rpn_mutation.h
  21. 387
    0
      rpn_parse.c
  22. 326
    0
      rpn_parse.h
  23. 257
    0
      test.c
  24. 0
    0
      tests/__init__.py
  25. 51
    0
      tests/benchmark.py
  26. 1
    0
      tests/pyrpn.so
  27. 275
    0
      tests/tests_rpn.py

+ 258
- 0
Doxyfile.mk View File

@@ -0,0 +1,258 @@
1
+DOXYFILE_ENCODING      = UTF-8
2
+PROJECT_NAME           = pyrpn
3
+PROJECT_BRIEF          = "RPN expression JIT compilation & evaluation"
4
+OUTPUT_DIRECTORY       = doc
5
+CREATE_SUBDIRS         = NO
6
+ALLOW_UNICODE_NAMES    = NO
7
+OUTPUT_LANGUAGE        = English
8
+BRIEF_MEMBER_DESC      = YES
9
+REPEAT_BRIEF           = YES
10
+ABBREVIATE_BRIEF       = "The $name class" \
11
+                         "The $name widget" \
12
+                         "The $name file" \
13
+                         is \
14
+                         provides \
15
+                         specifies \
16
+                         contains \
17
+                         represents \
18
+                         a \
19
+                         an \
20
+                         the
21
+ALWAYS_DETAILED_SEC    = NO
22
+INLINE_INHERITED_MEMB  = NO
23
+FULL_PATH_NAMES        = YES
24
+SHORT_NAMES            = NO
25
+JAVADOC_AUTOBRIEF      = NO
26
+QT_AUTOBRIEF           = NO
27
+MULTILINE_CPP_IS_BRIEF = NO
28
+INHERIT_DOCS           = YES
29
+SEPARATE_MEMBER_PAGES  = NO
30
+TAB_SIZE               = 4
31
+OPTIMIZE_OUTPUT_FOR_C  = YES
32
+OPTIMIZE_OUTPUT_JAVA   = NO
33
+OPTIMIZE_FOR_FORTRAN   = NO
34
+OPTIMIZE_OUTPUT_VHDL   = NO
35
+MARKDOWN_SUPPORT       = YES
36
+TOC_INCLUDE_HEADINGS   = 0
37
+AUTOLINK_SUPPORT       = YES
38
+BUILTIN_STL_SUPPORT    = NO
39
+CPP_CLI_SUPPORT        = NO
40
+SIP_SUPPORT            = NO
41
+IDL_PROPERTY_SUPPORT   = YES
42
+DISTRIBUTE_GROUP_DOC   = NO
43
+GROUP_NESTED_COMPOUNDS = NO
44
+SUBGROUPING            = YES
45
+INLINE_GROUPED_CLASSES = NO
46
+INLINE_SIMPLE_STRUCTS  = NO
47
+TYPEDEF_HIDES_STRUCT   = NO
48
+LOOKUP_CACHE_SIZE      = 0
49
+EXTRACT_ALL            = YES
50
+EXTRACT_PRIVATE        = NO
51
+EXTRACT_PACKAGE        = NO
52
+EXTRACT_STATIC         = NO
53
+EXTRACT_LOCAL_CLASSES  = YES
54
+EXTRACT_LOCAL_METHODS  = NO
55
+EXTRACT_ANON_NSPACES   = NO
56
+HIDE_UNDOC_MEMBERS     = NO
57
+HIDE_UNDOC_CLASSES     = NO
58
+HIDE_FRIEND_COMPOUNDS  = NO
59
+HIDE_IN_BODY_DOCS      = NO
60
+INTERNAL_DOCS          = NO
61
+CASE_SENSE_NAMES       = NO
62
+HIDE_SCOPE_NAMES       = YES
63
+HIDE_COMPOUND_REFERENCE= NO
64
+SHOW_INCLUDE_FILES     = YES
65
+SHOW_GROUPED_MEMB_INC  = NO
66
+FORCE_LOCAL_INCLUDES   = NO
67
+INLINE_INFO            = YES
68
+SORT_MEMBER_DOCS       = YES
69
+SORT_BRIEF_DOCS        = NO
70
+SORT_MEMBERS_CTORS_1ST = NO
71
+SORT_GROUP_NAMES       = NO
72
+SORT_BY_SCOPE_NAME     = NO
73
+STRICT_PROTO_MATCHING  = NO
74
+GENERATE_TODOLIST      = YES
75
+GENERATE_TESTLIST      = YES
76
+GENERATE_BUGLIST       = YES
77
+GENERATE_DEPRECATEDLIST= YES
78
+MAX_INITIALIZER_LINES  = 30
79
+SHOW_USED_FILES        = YES
80
+SHOW_FILES             = YES
81
+SHOW_NAMESPACES        = YES
82
+QUIET                  = NO
83
+WARNINGS               = YES
84
+WARN_IF_UNDOCUMENTED   = YES
85
+WARN_IF_DOC_ERROR      = YES
86
+WARN_NO_PARAMDOC       = NO
87
+WARN_AS_ERROR          = NO
88
+WARN_FORMAT            = "$file:$line: $text"
89
+INPUT                  = ./
90
+INPUT_ENCODING         = UTF-8
91
+FILE_VERSION_FILTER    = "git log --format='rev:%h' -- "
92
+FILE_PATTERNS          = *.c \
93
+                         *.cc \
94
+                         *.cxx \
95
+                         *.cpp \
96
+                         *.c++ \
97
+                         *.java \
98
+                         *.ii \
99
+                         *.ixx \
100
+                         *.ipp \
101
+                         *.i++ \
102
+                         *.inl \
103
+                         *.idl \
104
+                         *.ddl \
105
+                         *.odl \
106
+                         *.h \
107
+                         *.hh \
108
+                         *.hxx \
109
+                         *.hpp \
110
+                         *.h++ \
111
+                         *.cs \
112
+                         *.d \
113
+                         *.php \
114
+                         *.php4 \
115
+                         *.php5 \
116
+                         *.phtml \
117
+                         *.inc \
118
+                         *.m \
119
+                         *.markdown \
120
+                         *.md \
121
+                         *.mm \
122
+                         *.dox \
123
+                         *.py \
124
+                         *.pyw \
125
+                         *.f90 \
126
+                         *.f95 \
127
+                         *.f03 \
128
+                         *.f08 \
129
+                         *.f \
130
+                         *.for \
131
+                         *.tcl \
132
+                         *.vhd \
133
+                         *.vhdl \
134
+                         *.ucf \
135
+			 *.asm \
136
+                         *.qsf
137
+RECURSIVE              = NO
138
+EXCLUDE                = test.c
139
+EXCLUDE_SYMLINKS       = NO
140
+EXAMPLE_PATTERNS       = *
141
+EXAMPLE_RECURSIVE      = NO
142
+FILTER_SOURCE_FILES    = NO
143
+SOURCE_BROWSER         = YES
144
+INLINE_SOURCES         = NO
145
+STRIP_CODE_COMMENTS    = YES
146
+REFERENCED_BY_RELATION = YES
147
+REFERENCES_RELATION    = YES
148
+REFERENCES_LINK_SOURCE = YES
149
+SOURCE_TOOLTIPS        = YES
150
+USE_HTAGS              = NO
151
+VERBATIM_HEADERS       = YES
152
+CLANG_ASSISTED_PARSING = NO
153
+ALPHABETICAL_INDEX     = YES
154
+COLS_IN_ALPHA_INDEX    = 5
155
+GENERATE_HTML          = YES
156
+HTML_OUTPUT            = html
157
+HTML_FILE_EXTENSION    = .html
158
+HTML_COLORSTYLE_HUE    = 220
159
+HTML_COLORSTYLE_SAT    = 100
160
+HTML_COLORSTYLE_GAMMA  = 80
161
+HTML_TIMESTAMP         = NO
162
+HTML_DYNAMIC_SECTIONS  = NO
163
+HTML_INDEX_NUM_ENTRIES = 100
164
+GENERATE_DOCSET        = NO
165
+DOCSET_FEEDNAME        = "Doxygen generated docs"
166
+DOCSET_BUNDLE_ID       = org.doxygen.Project
167
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
168
+DOCSET_PUBLISHER_NAME  = Publisher
169
+GENERATE_HTMLHELP      = NO
170
+GENERATE_CHI           = NO
171
+BINARY_TOC             = NO
172
+TOC_EXPAND             = NO
173
+GENERATE_QHP           = NO
174
+QHP_NAMESPACE          = org.doxygen.Project
175
+QHP_VIRTUAL_FOLDER     = doc
176
+GENERATE_ECLIPSEHELP   = NO
177
+ECLIPSE_DOC_ID         = org.doxygen.Project
178
+DISABLE_INDEX          = NO
179
+GENERATE_TREEVIEW      = NO
180
+ENUM_VALUES_PER_LINE   = 4
181
+TREEVIEW_WIDTH         = 250
182
+EXT_LINKS_IN_WINDOW    = NO
183
+FORMULA_FONTSIZE       = 10
184
+FORMULA_TRANSPARENT    = YES
185
+USE_MATHJAX            = NO
186
+MATHJAX_FORMAT         = HTML-CSS
187
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
188
+SEARCHENGINE           = YES
189
+SERVER_BASED_SEARCH    = NO
190
+EXTERNAL_SEARCH        = NO
191
+SEARCHDATA_FILE        = searchdata.xml
192
+GENERATE_LATEX         = NO
193
+LATEX_OUTPUT           = latex
194
+LATEX_CMD_NAME         = latex
195
+MAKEINDEX_CMD_NAME     = makeindex
196
+COMPACT_LATEX          = NO
197
+PAPER_TYPE             = a4
198
+PDF_HYPERLINKS         = YES
199
+USE_PDFLATEX           = YES
200
+LATEX_BATCHMODE        = NO
201
+LATEX_HIDE_INDICES     = NO
202
+LATEX_SOURCE_CODE      = NO
203
+LATEX_BIB_STYLE        = plain
204
+LATEX_TIMESTAMP        = NO
205
+GENERATE_RTF           = NO
206
+RTF_OUTPUT             = rtf
207
+COMPACT_RTF            = NO
208
+RTF_HYPERLINKS         = NO
209
+RTF_SOURCE_CODE        = NO
210
+GENERATE_MAN           = YES
211
+MAN_OUTPUT             = man
212
+MAN_EXTENSION          = .3
213
+MAN_LINKS              = NO
214
+GENERATE_XML           = NO
215
+XML_OUTPUT             = xml
216
+XML_PROGRAMLISTING     = YES
217
+GENERATE_DOCBOOK       = NO
218
+DOCBOOK_OUTPUT         = docbook
219
+DOCBOOK_PROGRAMLISTING = NO
220
+GENERATE_AUTOGEN_DEF   = NO
221
+GENERATE_PERLMOD       = NO
222
+PERLMOD_LATEX          = NO
223
+PERLMOD_PRETTY         = YES
224
+ENABLE_PREPROCESSING   = YES
225
+MACRO_EXPANSION        = YES
226
+EXPAND_ONLY_PREDEF     = NO
227
+SEARCH_INCLUDES        = YES
228
+SKIP_FUNCTION_MACROS   = YES
229
+ALLEXTERNALS           = NO
230
+EXTERNAL_GROUPS        = YES
231
+EXTERNAL_PAGES         = YES
232
+PERL_PATH              = /usr/bin/perl
233
+CLASS_DIAGRAMS         = YES
234
+HIDE_UNDOC_RELATIONS   = YES
235
+HAVE_DOT               = YES
236
+DOT_NUM_THREADS        = 0
237
+DOT_FONTNAME           = Helvetica
238
+DOT_FONTSIZE           = 10
239
+CLASS_GRAPH            = YES
240
+COLLABORATION_GRAPH    = YES
241
+GROUP_GRAPHS           = YES
242
+UML_LOOK               = NO
243
+UML_LIMIT_NUM_FIELDS   = 10
244
+TEMPLATE_RELATIONS     = NO
245
+INCLUDE_GRAPH          = YES
246
+INCLUDED_BY_GRAPH      = YES
247
+CALL_GRAPH             = YES
248
+CALLER_GRAPH           = YES
249
+GRAPHICAL_HIERARCHY    = YES
250
+DIRECTORY_GRAPH        = YES
251
+DOT_IMAGE_FORMAT       = svg
252
+INTERACTIVE_SVG        = YES
253
+DOT_GRAPH_MAX_NODES    = 50
254
+MAX_DOT_GRAPH_DEPTH    = 0
255
+DOT_TRANSPARENT        = NO
256
+DOT_MULTI_TARGETS      = NO
257
+GENERATE_LEGEND        = YES
258
+DOT_CLEANUP            = YES

+ 9
- 0
LICENCE.txt View File

@@ -0,0 +1,9 @@
1
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
2
+<html><head>
3
+<title>302 Found</title>
4
+</head><body>
5
+<h1>Found</h1>
6
+<p>The document has moved <a href="https://www.gnu.org/licenses/gpl-3.0.txt">here</a>.</p>
7
+<hr>
8
+<address>Apache/2.4.7 Server at www.gnu.org Port 443</address>
9
+</body></html>

+ 91
- 0
Makefile View File

@@ -0,0 +1,91 @@
1
+CC=gcc
2
+NASM=nasm
3
+LD=ld
4
+
5
+ifeq ($(DEBUG), 1)
6
+	CFLAGS=-ggdb -fPIC -Wall -DDEBUG
7
+	LDFLAGS=-g
8
+	NASMCFLAGS=-g -f elf64
9
+	#PYTHON=python3dm
10
+	PYTHON=python3-dbg
11
+else
12
+	CFLAGS=-fPIC -Wall -Werror
13
+	LDFLAGS=-s
14
+	NASMCFLAGS=-f elf64
15
+	PYTHON=python3
16
+endif
17
+
18
+PYTHON_CONFIG=$(PYTHON)-config
19
+PYTHON_CFLAGS=`$(PYTHON_CONFIG) --includes` `$(PYTHON_CONFIG) --cflags`
20
+PYTHON_LDFLAGS=-shared  -fPIC `$(PYTHON_CONFIG) --libs` `$(PYTHON_CONFIG) --ldflags|cut -d' ' -f1,2`
21
+
22
+all: .deps pyrpn.so
23
+
24
+pyrpn.so: python_pyrpn.o python_rpnexpr.o rpn_lib.o rpn_jit.o rpn_parse.o rpn_mutation.o rpn_if.o
25
+	$(LD) $(LDFLAGS) $(PYTHON_LDFLAGS) -o $@ $^
26
+
27
+python_pyrpn.o: python_pyrpn.c python_rpnexpr.h python_rpnexpr.o rpn_jit.o
28
+	$(CC) $(PYTHON_CFLAGS) $(CFLAGS) -c $<
29
+
30
+python_rpnexpr.o: python_rpnexpr.c python_rpnexpr.h rpn_jit.o
31
+	$(CC) $(PYTHON_CFLAGS) $(CFLAGS) -c $<
32
+
33
+rpn_jit.o: rpn_jit.c rpn_jit.h rpn_parse.o rpn_lib.o
34
+	$(CC) $(CFLAGS) -c $<
35
+
36
+rpn_parse.o: rpn_parse.c rpn_parse.h rpn_lib.o
37
+	$(CC) $(CFLAGS) -c $<
38
+
39
+rpn_mutation.o: rpn_mutation.c rpn_mutation.h rpn_parse.o
40
+	$(CC) $(CFLAGS) -c $<
41
+
42
+rpn_if.o: rpn_if.c rpn_if.h rpn_jit.o
43
+	$(CC) $(CFLAGS) -c $<
44
+
45
+rpn_lib.o: rpn_lib.asm rpn_lib.h
46
+	$(NASM) $(NASMCFLAGS) -o $@ $<
47
+
48
+# Dirty & quick tests
49
+test: test.o rpn_lib.o rpn_jit.o rpn_parse.o
50
+	$(CC) $(CFLAGS) -o $@ $^
51
+
52
+test.o: test.c
53
+	$(CC) $(CFLAGS) -c -o $@ $<
54
+
55
+# Doxygen documentation
56
+doc: doc/.doxygen.stamp
57
+
58
+Doxyfile: Doxyfile.mk
59
+	echo "PROJECT_NUMBER        = `cat VERSION`_rev:`git rev-parse --short HEAD`" > $@
60
+	cat $< >> $@
61
+
62
+doc/.doxygen.stamp: $(wildcard *.c) $(wildcard *.h) Doxyfile
63
+	touch doc/.doxygen.stamp
64
+	doxygen 1>/dev/null
65
+	echo "Documentation in file://`pwd`/doc/html/index.html"
66
+
67
+# Dependencies checking
68
+.deps: check_deps.sh
69
+	sh check_deps.sh "$(CC)" "$(LD)" "$(NASM)" "$(PYTHON)" "$(PYTHON_CONFIG)" && touch .deps
70
+
71
+.PHONY: clean distclean checks runtest unittest benchmark
72
+
73
+checks: runtest unittest benchmark
74
+
75
+benchmark: pyrpn.so
76
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py
77
+
78
+unittest: pyrpn.so
79
+	PYTHONPATH=`pwd` $(PYTHON) -m unittest
80
+
81
+runtest: test
82
+	./test
83
+
84
+clean:
85
+	-rm -fv *.o pyrpn.so test
86
+	-rm -fRv doc/.doxygen.stamp doc/* Doxyfile
87
+
88
+distclean: clean
89
+	-rm -vf .deps
90
+	-rm -Rvf tests/__pycache__
91
+

+ 45
- 0
README View File

@@ -0,0 +1,45 @@
1
+rpnifs fast IFS using RPN notation :
2
+====================================
3
+
4
+Provides :
5
+----------
6
+- C library for parameterized RPN expression JIT compilation and evaluation
7
+- C library for handling IFS (composed of JIT RPN expressions)
8
+- C library for RPN expression random mutation
9
+- Python bindings : pyrpn Python module (pyrpn.so)
10
+
11
+More details on Python module by running :
12
+	make && python3 -c "import pyrpn; help(pyrpn)"
13
+
14
+More details on C API see Doxygen documentation.
15
+
16
+Dependencies :
17
+--------------
18
+- gcc
19
+- nasm
20
+- ld
21
+- python3 
22
+- python3 headers
23
+
24
+Documentation :
25
+- doxygen
26
+- git
27
+
28
+Compilation :
29
+-------------
30
+	make
31
+
32
+Doxygen documentation :
33
+-----------------------
34
+	make doc
35
+	www-browser doc/html/index.html
36
+
37
+Running self tests and benchmark :
38
+----------------------------------
39
+	make checks
40
+
41
+Debugging :
42
+-----------
43
+	make clean
44
+	DEBUG=1 make
45
+	DEBUG=1 make check

+ 1
- 0
VERSION View File

@@ -0,0 +1 @@
1
+0.0.1-dev

+ 55
- 0
check_deps.sh View File

@@ -0,0 +1,55 @@
1
+#!/bin/sh
2
+
3
+if [ "$#" -ne 5 ]
4
+then
5
+	echo "Usage : $0 CC LD NASM PYTHON PYTHON-CONFIG" >&2
6
+	exit 1
7
+fi
8
+
9
+CC=$1
10
+LD=$2
11
+NASM=$3
12
+PYTHON=$4
13
+PYTHON_CONFIG=$5
14
+
15
+error=""
16
+for i in `seq $#`
17
+do
18
+	deps=$1
19
+	shift 1
20
+	echo -n "Searching $deps :\t"
21
+	which $deps
22
+	if [ "r$?" != "r0" ]
23
+	then
24
+		echo " NOT FOUND"
25
+		error="$error\n$deps not found"
26
+	fi
27
+done
28
+
29
+if which $CC $PYTHON_CONFIG > /dev/null
30
+then
31
+	echo -n "Checking C compiler : "
32
+	echo '#define PY_SSIZE_T_CLEAN
33
+#include <Python.h>
34
+#include "structmember.h"
35
+int main() { Py_Initialize(); return 0; }' | $CC `$PYTHON_CONFIG --cflags` -xc -c -o /dev/null -
36
+	if [ "r$?" != "r0" ]
37
+	then
38
+		err="Unable to compile a Python program : missing python headers ?"
39
+		error="$error\n$err"
40
+		echo $err
41
+	else
42
+		echo "$CC compile with $PYTHON_CONFIG OK"
43
+	fi
44
+fi
45
+
46
+
47
+if [ -n "$error" ]
48
+then
49
+	echo -n "\n======== ERRORS ========"
50
+	echo "$error"
51
+	exit 1
52
+fi
53
+exit 0
54
+
55
+

+ 22
- 0
config.h View File

@@ -0,0 +1,22 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __RPN_CONFIG__
20
+#define __RPN_CONFIG__
21
+#define _GNU_SOURCE
22
+#endif

+ 129
- 0
python_pyrpn.c View File

@@ -0,0 +1,129 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "python_pyrpn.h"
20
+
21
+/**@file python_pyrpn.c
22
+ * @brief Python module & type definition
23
+ * @ingroup python_ext
24
+ *
25
+ * This file contains pyrpn Python module definition
26
+ */
27
+
28
+PyMethodDef rpnmodule_methods[] = {
29
+	{"get_ops", (PyCFunction)pyrpn_ops, METH_NOARGS,
30
+		"Returns a valid operands dict"},
31
+	{"random_expr", (PyCFunction)pyrpn_random, METH_VARARGS | METH_KEYWORDS,
32
+		"Return a random RPN expression"},
33
+	{NULL} // Sentinel
34
+};
35
+
36
+PyModuleDef rpnmodule = {
37
+	PyModuleDef_HEAD_INIT,
38
+	"pyrpn",
39
+	"Python librarie for RPN evaluation",
40
+	-1,	// module size
41
+	rpnmodule_methods,
42
+	NULL,	// m_slots
43
+	NULL,	// m_traverse
44
+	NULL,	// m_clear
45
+	NULL	// m_free
46
+};
47
+
48
+PyMODINIT_FUNC
49
+PyInit_pyrpn(void)
50
+{
51
+
52
+	PyObject *mod;
53
+	// init module & globals
54
+	mod = PyModule_Create(&rpnmodule);
55
+	if(mod == NULL) { return NULL; }
56
+	
57
+	// Init RPNExpr type
58
+	if(PyType_Ready(&RPNExprType) < 0)
59
+	{
60
+		return NULL;
61
+	}
62
+
63
+	// Add type to module
64
+	Py_INCREF(&RPNExprType);
65
+	if(PyModule_AddObject(mod, "RPNExpr", (PyObject*)&RPNExprType) < 0)
66
+	{
67
+		Py_DECREF(&RPNExprType);
68
+		Py_DECREF(mod);
69
+		return NULL;
70
+	}
71
+
72
+	return mod;
73
+}
74
+
75
+PyObject* pyrpn_ops(PyObject* mod, PyObject* noargs)
76
+{
77
+	PyObject *ret, *value;
78
+	const rpn_op_t *op;
79
+	size_t i;
80
+
81
+	ret = PyDict_New();
82
+	if(!ret)
83
+	{
84
+		return ret;
85
+	}
86
+
87
+	foreach_rpn_ops(i)
88
+	{
89
+		op = &rpn_ops[i];
90
+		value = Py_BuildValue("C", op->chr);
91
+		if(PyDict_SetItemString(ret, op->str, value))
92
+		{
93
+			Py_DECREF(value);
94
+			Py_DECREF(ret);
95
+			return NULL;
96
+		}
97
+		Py_DECREF(value);
98
+	}
99
+	
100
+	return ret;
101
+}
102
+
103
+PyObject* pyrpn_random(PyObject *mod, PyObject *args, PyObject *kwds)
104
+{
105
+	long long int args_count, expr_sz;
106
+	char *expr, err_str[128];
107
+	PyObject *res;
108
+	char *names[] = {"args_count", "token_count", NULL};
109
+
110
+	expr_sz = 10;
111
+	if(!PyArg_ParseTupleAndKeywords(args, kwds, "L|L:pyrpn.random_expr", names,
112
+		&args_count, &expr_sz))
113
+	{
114
+		return NULL;
115
+	}
116
+	
117
+	expr = rpn_random(expr_sz, args_count);
118
+	if(!expr)
119
+	{
120
+		snprintf(err_str, 128,
121
+			"Error generating random expression : %s",
122
+			strerror(errno));
123
+		PyErr_SetString(PyExc_RuntimeError, err_str);
124
+		return NULL;
125
+	}
126
+	res = Py_BuildValue("s", expr);
127
+	//free(expr);
128
+	return res;
129
+}

+ 96
- 0
python_pyrpn.h View File

@@ -0,0 +1,96 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef _PYTHON_PYRPN_H__
20
+#define _PYTHON_PYRPN_H__
21
+
22
+#include "config.h"
23
+
24
+#include <errno.h>
25
+
26
+#define PY_SSIZE_T_CLEAN
27
+#include <Python.h>
28
+#include "structmember.h"
29
+
30
+#include "rpn_jit.h"
31
+#include "python_rpnexpr.h"
32
+
33
+/**@defgroup python_ext Python API
34
+ * @brief Python API definitions
35
+ *
36
+ * @ref python_pyrpn.c and @ref python_rpnexpr.c should be compiled in a .so file
37
+ * in order to expose a pyrpn Python module.
38
+ *
39
+ * This module contains functions :
40
+ * - pyrpn.get_ops() returning a dict with long & short operations
41
+ * - pyrpn.random_expr(args_count, token_count=10) generating a random expression
42
+ *
43
+ * And it contains the pyrpn.RPNExpr class with following methods :
44
+ * - pyrpn.RPNExpr.__init__(self, expression, args_count, stack_size=16)
45
+ * - pyrpn.RPNExpr.eval(self, ...) expression evaluation
46
+ * - pyrpn.RPNExpr.reset_stack(self) to set all stack items to 0
47
+ */
48
+
49
+/**@defgroup python_module pyrpn Python module
50
+ * @brief Exposed Python module : pyrpn
51
+ * @ingroup python_ext
52
+ */
53
+
54
+/**@file python_pyrpn.h
55
+ * @brief Python module & type headers
56
+ * @ingroup python_module
57
+ *
58
+ * This file is the header of the pyrpn Python module definition
59
+ */
60
+
61
+extern PyTypeObject RPNExprType;
62
+
63
+/**@brief Python module initialization function
64
+ * @ingroup python_module */
65
+PyMODINIT_FUNC PyInit_pyrpn(void);
66
+/**@brief pyrpn module methods list
67
+ * @ingroup python_module */
68
+extern PyMethodDef rpnmodule_methods[];
69
+/**@brief Python module specs
70
+ * @ingroup python_module */
71
+extern PyModuleDef rpnmodule;
72
+
73
+/**@brief Return a dict with valid operations (short keys and long values)
74
+ * @param mod pyrpn module object
75
+ * @param noargs Dummy argument for METH_NOARG
76
+ * @return The a new dict
77
+ * @ingroup python_module
78
+ */
79
+PyObject* pyrpn_ops(PyObject* mod, PyObject* noargs);
80
+
81
+/**@brief Return a new Python str with a random RPN expression
82
+ * @param mod pyrpn module object
83
+ * @param args Position arguments in Python list
84
+ * @param kwds Keywords arguments in Python dict
85
+ * @ingroup python_module
86
+ */
87
+PyObject* pyrpn_random(PyObject *mod, PyObject *args, PyObject *kwds);
88
+
89
+/**@mainpage
90
+ * Documentation entrypoints :
91
+ * - @ref python_ext
92
+ * - @ref rpn_lang
93
+ * - @ref rpn
94
+ */
95
+
96
+#endif

+ 283
- 0
python_rpnexpr.c View File

@@ -0,0 +1,283 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "python_rpnexpr.h"
20
+
21
+PyMethodDef RPNExpr_methods[] = {
22
+	{"eval", (PyCFunction)rpnexpr_eval, METH_FASTCALL, "Evaluate an expression"},
23
+	{"reset_stack", (PyCFunction)rpnexpr_reset_stack, METH_NOARGS,
24
+		"Reset stack memory storage (set all items to 0)"},
25
+	{NULL} //Sentinel
26
+};
27
+
28
+PyMemberDef RPNExpr_members[] = {
29
+	{NULL}
30
+};
31
+
32
+PyTypeObject RPNExprType = {
33
+	PyVarObject_HEAD_INIT(NULL, 0)
34
+	"rpn.RPNExpr",                     /* tp_name */
35
+	sizeof(PyRPNExpr_t),                        /* tp_basicsize */
36
+	0,                                               /* tp_itemsize */
37
+	(destructor)rpnexpr_del, /* tp_dealloc */
38
+	0,                                               /* tp_print */
39
+	0,                                               /* tp_getattr */
40
+	0,                                               /* tp_setattr */
41
+	0,                                               /* tp_reserved */
42
+	rpnexpr_repr,                                    /* tp_repr */
43
+	0,                                               /* tp_as_number */
44
+	0,                                               /* tp_as_sequence */
45
+	0,                              /* tp_as_mapping */
46
+	0,                                               /* tp_hash  */
47
+	0,                                               /* tp_call */
48
+	rpnexpr_str,                                               /* tp_str */
49
+	0,                                               /* tp_getattro */
50
+	0,                                               /* tp_setattro */
51
+	0,                                               /* tp_as_buffer */
52
+	Py_TPFLAGS_DEFAULT |
53
+	Py_TPFLAGS_BASETYPE,   /* tp_flags */
54
+	"RPN expression evaluator",                /* tp_doc */
55
+	0,                                               /* tp_traverse */
56
+	0,                                               /* tp_clear */
57
+	0,                                               /* tp_richcompare */
58
+	0,                                               /* tp_weaklistoffset */
59
+	0,                                               /* tp_iter */
60
+	0,                                               /* tp_iternext */
61
+	RPNExpr_methods,                        /* tp_methods */
62
+	RPNExpr_members,                        /* tp_members */
63
+	0,                                               /* tp_getset */
64
+	0,                                               /* tp_base */
65
+	0,                                               /* tp_dict */
66
+	0,                                               /* tp_descr_get */
67
+	0,                                               /* tp_descr_set */
68
+	0,                                               /* tp_dictoffset */
69
+	rpnexpr_init,          /* tp_init */
70
+	0,                                               /* tp_alloc */
71
+	rpnexpr_new,                            /* tp_new */
72
+};
73
+
74
+PyObject* rpnexpr_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds)
75
+{
76
+	PyObject *ret, *err;
77
+	PyRPNExpr_t *expr;
78
+	ret = PyType_GenericNew(subtype, args, kwds);
79
+	if((err = PyErr_Occurred()))
80
+	{
81
+		Py_DECREF(err);
82
+		return ret;
83
+	}
84
+	expr = (PyRPNExpr_t*)ret;
85
+	expr->rpn = NULL;
86
+	expr->args = NULL;
87
+	return ret;
88
+}
89
+
90
+int rpnexpr_init(PyObject *self, PyObject *args, PyObject *kwds)
91
+{
92
+	PyRPNExpr_t *expr_self;
93
+	char *names[] = {"expression", "args_count", "stack_size", NULL};
94
+	char err_str[256];
95
+	const char *expr;
96
+	long long int args_count, stack_size;
97
+
98
+	expr_self = (PyRPNExpr_t*)self;
99
+
100
+	stack_size = 16;
101
+	expr_self->rpn = NULL;
102
+
103
+	if(!PyArg_ParseTupleAndKeywords(args, kwds, "sL|L:RPNExpr.__init__", names, &expr,
104
+		&args_count, &stack_size))
105
+	{
106
+		return -1;
107
+	}
108
+
109
+	if(strlen(expr) == 0)
110
+	{
111
+		PyErr_SetString(PyExc_ValueError,
112
+			"RpnExpr.__init__() expect expression argument to be not empty");
113
+		return -1;
114
+	}
115
+
116
+	if(args_count < 0 || args_count > 255)
117
+	{
118
+		snprintf(err_str, 128,
119
+			"Argument count should be in [4..255] but %lld given",
120
+			args_count);
121
+		PyErr_SetString(PyExc_ValueError, err_str);
122
+		return -1;
123
+	}
124
+
125
+	if(stack_size < 4 || stack_size > 255)
126
+	{
127
+		snprintf(err_str, 128,
128
+			"Stack size should be in [0..255] but %lld given",
129
+			stack_size);
130
+		PyErr_SetString(PyExc_ValueError, err_str);
131
+		return -1;
132
+	}
133
+
134
+	expr_self->rpn = malloc(sizeof(rpn_expr_t));
135
+	if(!expr_self->rpn)
136
+	{
137
+		snprintf(err_str, 256,
138
+			"Expression memory allocation error : %s",
139
+			strerror(errno));
140
+	}
141
+	bzero(expr_self->rpn, sizeof(rpn_expr_t));
142
+
143
+	if(rpn_expr_init(expr_self->rpn, stack_size, args_count) < 0)
144
+	{
145
+		snprintf(err_str, 256,
146
+			"Expression init error : %s",
147
+			expr_self->rpn->err_reason);
148
+		PyErr_SetString(PyExc_ValueError, err_str);
149
+		return -1;
150
+	}
151
+
152
+	if(!stack_size)
153
+	{
154
+		expr_self->args = NULL;
155
+		return 0;
156
+	}
157
+
158
+	expr_self->args = malloc(sizeof(unsigned long) * args_count);
159
+	if(!expr_self->args)
160
+	{
161
+		snprintf(err_str, 256,
162
+			"Error allocating arguments memory : %s",
163
+			strerror(errno));
164
+		return -1;
165
+	}
166
+
167
+	if(rpn_expr_compile(expr_self->rpn, expr))
168
+	{
169
+		PyErr_SetString(PyExc_ValueError, expr_self->rpn->err_reason);
170
+		return -1;
171
+	}
172
+
173
+	return 0;
174
+}
175
+
176
+void rpnexpr_del(PyObject *self)
177
+{
178
+	PyRPNExpr_t *expr_self;
179
+
180
+	expr_self = (PyRPNExpr_t*)self;
181
+	if(expr_self->rpn)
182
+	{
183
+		rpn_expr_close(expr_self->rpn);
184
+		free(expr_self->rpn);
185
+		expr_self->rpn = NULL;
186
+	}
187
+	if(expr_self->args)
188
+	{
189
+		free(expr_self->args);
190
+		expr_self->args = NULL;
191
+	}
192
+}
193
+
194
+PyObject* rpnexpr_eval(PyObject* self, PyObject** argv, Py_ssize_t argc)
195
+{
196
+	PyRPNExpr_t *expr_self;
197
+	unsigned long res;
198
+	char err_str[128];
199
+	Py_ssize_t i;
200
+	PyObject *cur, *ret;
201
+
202
+
203
+	expr_self = (PyRPNExpr_t*)self;
204
+
205
+	if((unsigned long)argc != expr_self->rpn->args_count)
206
+	{
207
+		snprintf(err_str, 128,
208
+			"RPNExpr expected %ld arguments but %ld given",
209
+			expr_self->rpn->args_count,
210
+			argc);
211
+		PyErr_SetString(PyExc_ValueError, err_str);
212
+		return NULL;
213
+	}
214
+
215
+	for(i=0; i<argc; i++)
216
+	{
217
+		cur = argv[i];
218
+		if(!PyLong_Check(cur))
219
+		{
220
+			snprintf(err_str, 128,
221
+"RpnExpr.__call__ expect int as arguments but argument %ld is not",
222
+				i+1);
223
+			PyErr_SetString(PyExc_ValueError, err_str);
224
+			return NULL;
225
+		}
226
+		expr_self->args[i] = PyLong_AsUnsignedLong(argv[i]);
227
+		if((ret = PyErr_Occurred()))
228
+		{
229
+			Py_DECREF(ret);
230
+			return NULL;
231
+		}
232
+	}
233
+
234
+	res = rpn_expr_eval(expr_self->rpn, expr_self->args);
235
+//dprintf(2, "[RES=%lu]\n", res);
236
+
237
+	return PyLong_FromUnsignedLong(res);
238
+}
239
+
240
+PyObject* rpnexpr_reset_stack(PyObject *self, PyObject *noargs)
241
+{
242
+	rpn_expr_reset_stack(((PyRPNExpr_t*)self)->rpn);
243
+	Py_RETURN_NONE;
244
+}
245
+
246
+PyObject* rpnexpr_str(PyObject *self)
247
+{
248
+	PyRPNExpr_t *expr_self;
249
+	PyObject *res;
250
+
251
+	expr_self = (PyRPNExpr_t*)self;
252
+	res = Py_BuildValue("s", expr_self->rpn->expr);
253
+	return res;
254
+}
255
+
256
+PyObject* rpnexpr_repr(PyObject *self)
257
+{
258
+	PyRPNExpr_t *expr_self;
259
+	PyObject *res;
260
+	size_t sz;
261
+	char *buff, err_str[128];
262
+
263
+	expr_self = (PyRPNExpr_t*)self;
264
+	sz = snprintf(NULL, 0, "<RPNExpr argc:%ld stck_sz:%d '%s'>",
265
+		expr_self->rpn->args_count, expr_self->rpn->stack_sz,
266
+		expr_self->rpn->expr);
267
+	buff = malloc(sizeof(char) * (sz + 1));
268
+	if(!buff)
269
+	{
270
+		snprintf(err_str, 128,
271
+			"Error allocating repr : %s",
272
+			strerror(errno));
273
+		PyErr_SetString(PyExc_RuntimeError, err_str);
274
+		return NULL;
275
+	}
276
+	snprintf(buff, sz+1, "<RPNExpr argc:%ld stck_sz:%d '%s'>",
277
+		expr_self->rpn->args_count, expr_self->rpn->stack_sz,
278
+		expr_self->rpn->expr);
279
+	res = Py_BuildValue("s", buff);
280
+	free(buff);
281
+	return res;
282
+}
283
+

+ 120
- 0
python_rpnexpr.h View File

@@ -0,0 +1,120 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef _PYTHON_PYRPN_H__
20
+#define _PYTHON_PYRPN_H__
21
+
22
+#include "config.h"
23
+
24
+#include <errno.h>
25
+
26
+#define PY_SSIZE_T_CLEAN
27
+#include <Python.h>
28
+#include "structmember.h"
29
+
30
+#include "rpn_jit.h"
31
+
32
+/**@defgroup python_type RPNExpr Python class
33
+ * @brief Exposed Python class : RPNExpr
34
+ * @ingroup python_ext
35
+ */
36
+
37
+/**@file python_rpnexpr.h
38
+ * @brief Python RPNExpr type headers
39
+ * @ingroup python_type
40
+ *
41
+ * This file is the header of the RPNExpr Python class
42
+ */
43
+
44
+/**@brief RPNExpr Python class methods list
45
+ * @ingroup python_type */
46
+extern PyMethodDef RPNExpr_methods[];
47
+/**@brief RPNExpr Python class members list
48
+ * @ingroup python_type */
49
+extern PyMemberDef RPNExpr_members[];
50
+/**@brief RPNExpr Python class type definition
51
+ * @ingroup python_type */
52
+extern PyTypeObject RPNExprType;
53
+
54
+/**@brief Structure holding RPNExpr objects
55
+ * @ingroup python_type */
56
+typedef struct
57
+{
58
+	PyObject_VAR_HEAD;
59
+	
60
+	/**@brief Pointer on @ref rpn_expr_s */
61
+	rpn_expr_t *rpn;
62
+
63
+	/**@brief Array storing expression argument
64
+	 * @note As attribute of rpn_expr allowing malloc & free only once */
65
+	unsigned long *args;
66
+} PyRPNExpr_t;
67
+
68
+/**@brief RpnExpr __new__ method
69
+ * @param subtype Type of object being created (pyrpn.RPNExpr)
70
+ * @param args positional arguments for subtype
71
+ * @param kwargs keyword argumenrs for subtype
72
+ * @return The new Python RPNExpr object
73
+ */
74
+PyObject* rpnexpr_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds);
75
+
76
+/**@brief RpnExpr constructor
77
+ * @param self New RPNExpr instance
78
+ * @param args Positional arguments list
79
+ * @param kwds Keywords arguments dict
80
+ * @return 0 if no error else -1
81
+ * @ingroup python_type
82
+ */
83
+int rpnexpr_init(PyObject *self, PyObject *args, PyObject *kwds);
84
+/**@brief RPNExpr __del__ method 
85
+ * @param self RPNExpr instance
86
+ * @ingroup python_type
87
+ */
88
+void rpnexpr_del(PyObject *self);
89
+
90
+/**@brief Eval an RPN expression given arguments and return the
91
+ * value
92
+ * @param self RPNExpr instance
93
+ * @param argv Array of PyObject* arguments
94
+ * @param argc Number of arguments
95
+ * @return The value resulting of evaluation
96
+ * @ingroup python_type
97
+ */
98
+PyObject* rpnexpr_eval(PyObject* self, PyObject** argv, Py_ssize_t argc);
99
+
100
+/**@brief Set all stack item to zero
101
+ * @param self RPNExpr instance
102
+ * @param noargs Dummy argument for METH_NOARG
103
+ * @return None
104
+ * @ingroup python_type
105
+ */
106
+PyObject* rpnexpr_reset_stack(PyObject *self, PyObject *noargs);
107
+
108
+/**@brief RPNExpr.__repr__()
109
+ * @param self RPNExpr instance
110
+ * @ingroup python_type
111
+ */
112
+PyObject* rpnexpr_repr(PyObject *self);
113
+
114
+/**@brief RPNExpr.__str__()
115
+ * @param self RPNExpr instance
116
+ * @ingroup python_type
117
+ */
118
+PyObject* rpnexpr_str(PyObject *self);
119
+
120
+#endif

+ 145
- 0
rpn_if.c View File

@@ -0,0 +1,145 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "rpn_if.h"
20
+
21
+rpn_if_t* rpn_if_new(size_t mem_sz, rpn_if_transfo_t *if2rpn,
22
+	rpn_if_transfo_t *rpn2if, rpn_expr_t *rpn)
23
+{
24
+	rpn_if_t *res;
25
+	if(mem_sz != 0)
26
+	{
27
+		if((if2rpn->mem_sz != 0 && if2rpn->mem_sz != mem_sz) ||
28
+			(rpn2if->mem_sz != 0 && rpn2if->mem_sz != mem_sz))
29
+		{
30
+			errno = EINVAL;
31
+			return NULL;
32
+		}
33
+	}
34
+	else
35
+	{
36
+		if(if2rpn->mem_sz == 0 && rpn2if->mem_sz == 0)
37
+		{
38
+			errno = EINVAL;
39
+			return NULL;
40
+		}
41
+		if(if2rpn->mem_sz != rpn2if->mem_sz && if2rpn->mem_sz != 0 &&
42
+			rpn2if->mem_sz != 0)
43
+		{
44
+			errno = EINVAL;
45
+			return NULL;
46
+		}
47
+		mem_sz = if2rpn->mem_sz != 0?if2rpn->mem_sz:rpn2if->mem_sz;
48
+	}
49
+	if(if2rpn->data_sz != rpn2if->data_sz || if2rpn->data_sz == 0)
50
+	{
51
+		errno = EINVAL;
52
+		return NULL;
53
+	}
54
+	res = _rpn_if_new(mem_sz, if2rpn->argc, rpn2if->argc, rpn2if->data_sz);
55
+	if(!res)
56
+	{
57
+		return NULL;
58
+	}
59
+	res->if2rpn = if2rpn->if2rpn;
60
+	res->rpn2if = rpn2if->rpn2if;
61
+	res->rpn = rpn;
62
+	return res;
63
+}
64
+
65
+void rpn_if_free(rpn_if_t* rif)
66
+{
67
+	size_t i;
68
+	for(i=0; i<rif->rpn_sz; i++)
69
+	{
70
+		rpn_expr_close(&(rif->rpn[i]));
71
+	}
72
+	free(rif->rpn);
73
+	free(rif->rpn_res);
74
+	munmap(rif->mem, rif->mem_sz);
75
+	free(rif);
76
+}
77
+
78
+int rpn_if_rpn_upd(rpn_if_t *rif, rpn_tokenized_t *rpns)
79
+{
80
+	return rpn_if_rpn_upd_rng(rif, rpns, rif->rpn_sz, 0);
81
+}
82
+
83
+int rpn_if_rpn_upd_rng(rpn_if_t *rif, rpn_tokenized_t *rpns, size_t sz,
84
+	size_t offset)
85
+{
86
+	size_t i;
87
+	for(i=offset; i<offset+sz; i++)
88
+	{
89
+		/**@todo continue implementation */
90
+	}
91
+	return 0;
92
+}
93
+
94
+rpn_if_t* _rpn_if_new(size_t mem_sz, size_t rpn_argc, size_t rpn_count, size_t
95
+	value_sz)
96
+{
97
+	rpn_if_t *res;
98
+	int err;
99
+
100
+	res = malloc(sizeof(rpn_if_t));
101
+	if(!res)
102
+	{
103
+		goto error;
104
+	}
105
+	
106
+	res->rpn_sz = rpn_count;
107
+	res->rpn_argc = rpn_argc;
108
+	res->mem_sz = mem_sz;
109
+	res->value_sz = value_sz;
110
+
111
+	res->mem = mmap(NULL, mem_sz, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
112
+	if(res->mem == (void*)-1)
113
+	{
114
+		goto mmap_err;
115
+	}
116
+
117
+	res->rpn_res = malloc(sizeof(unsigned long) * (rpn_count +rpn_argc));
118
+	if(!res->rpn_res)
119
+	{
120
+		goto rpn_malloc_err;
121
+	}
122
+	res->rpn_args = &(res->rpn_res[res->rpn_sz]);
123
+
124
+	res->rpn = malloc(sizeof(rpn_expr_t) * res->rpn_sz);
125
+	if(!res->rpn)
126
+	{
127
+		goto rpn_expr_err;
128
+	}
129
+
130
+	return res;
131
+
132
+	rpn_expr_err:
133
+		err = errno;
134
+		free(res->rpn_res);
135
+	rpn_malloc_err:
136
+		err = errno;
137
+		munmap(res->mem, mem_sz);
138
+	mmap_err:
139
+		err = errno;
140
+		free(res);
141
+	error:
142
+		err = errno;
143
+		errno = err;
144
+		return NULL;
145
+}

+ 220
- 0
rpn_if.h View File

@@ -0,0 +1,220 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_if__h__
20
+#define __rpn_if__h__
21
+
22
+#include "config.h"
23
+
24
+#include "rpn_jit.h"
25
+
26
+/**@defgroup ifs_if Iterated function
27
+ * @brief Iterated RPN expression
28
+ *
29
+ * A single Iterated Function implemented using an RPN expression.
30
+ *
31
+ * @note The goal is to optimize iteration writing the code in C and providing
32
+ * an high level Python API.
33
+ *
34
+ * For more details about IF see dedicated page : @ref doc_ifs_if
35
+ */
36
+
37
+typedef void* rpn_if_arg_t;
38
+typedef unsigned long rpn_arg_t;
39
+typedef struct rpn_if_s rpn_if_t;
40
+typedef struct rpn_if_res_s rpn_if_res_t;
41
+typedef struct rpn_if_state_s rpn_if_state_t;
42
+typedef struct rpn_if_transfo_s rpn_if_transfo_t;
43
+typedef enum rpn_if_transfo_type_e rpn_if_transfo_type_t;
44
+/**@brief IF state to RPN arguments transformation */
45
+typedef void (*if2rpn_f)(rpn_if_t *rif, rpn_if_state_t, rpn_arg_t*);
46
+/**@brief RPN arguments to IF state transformation */
47
+typedef rpn_if_res_t (*rpn2if_f)(rpn_if_t *rif, rpn_arg_t, rpn_if_state_t);
48
+
49
+/**@brief IF state
50
+ *
51
+ * Will always be something like :
52
+ * - memory adress/offset/index
53
+ * - value
54
+ */
55
+struct rpn_if_state_s
56
+{
57
+	/**@brief Data address */
58
+	size_t i;
59
+	/**@brief Data value(s) */
60
+	void *val;
61
+};
62
+
63
+/**@brief Indicate function type for @ref rpn_if_transfo_s */
64
+enum rpn_if_transfo_type_e
65
+{
66
+	/**@brief No transformation
67
+	 *
68
+	 * Default behavior is to copy data in args directly assuming
69
+	 * argc * sizeof(unsigned long) =  data_sz */
70
+	RPN_f_null,
71
+	/**@brief Transform RPN result into data */
72
+	RPN_f_rpn2if,
73
+	/**@brief Transform data into RPN result */
74
+	RPN_f_if2rpn
75
+};
76
+/**@brief Represent an IF transformation function
77
+ *
78
+ * Can transform data into arguments or arguments into data, depending
79
+ * on function type.
80
+ */
81
+struct rpn_if_transfo_s
82
+{
83
+	/**@brief Function pointer type
84
+	 * @note optionnal, for type checking
85
+	 */
86
+	rpn_if_transfo_type_t type;
87
+	/**@brief Data size (a @ref rpn_if_s::mem item ) in bytes */
88
+	size_t data_sz;
89
+	/**@brief Memory size in byte */
90
+	size_t mem_sz;
91
+	/**@brief RPN arg/result size
92
+	 *
93
+	 * - if type is RPN_if2rpn argc is the rpn expression argc
94
+	 * - if type is RPN_rpn2if argc is the rpn expression count (results
95
+	 * count)
96
+	 */
97
+	size_t argc;
98
+	union {
99
+		/**@brief IF state to RPN arguments transformation */
100
+		if2rpn_f if2rpn;
101
+		/**@brief RPN arguments to IF state transformation */
102
+		rpn2if_f rpn2if;
103
+	};
104
+};
105
+
106
+/**@brief Generic Iterated function implementation */
107
+struct rpn_if_s
108
+{
109
+	/**@brief Memory map in wich data are fetch & stored */
110
+	void *mem;
111
+	/**@brief Memory map size in bytes */
112
+	size_t mem_sz;
113
+	/**@brief Size of a memory item */
114
+	size_t value_sz;
115
+
116
+	/**@brief IF last position + value buffer */
117
+	rpn_if_state_t state;
118
+	/**@brief RPN expression(s) result(s) buffer */
119
+	unsigned long *rpn_res;
120
+	/**@brief Arguments given to RPN expression(s) buffer */
121
+	rpn_arg_t *rpn_args;
122
+	/**@brief Number of arguments expected by RPN expressions */
123
+	size_t rpn_argc;
124
+
125
+	/**@brief RPN expression(s) pointer(s) */
126
+	rpn_expr_t *rpn;
127
+	/**@brief RPN expression count */
128
+	size_t rpn_sz;
129
+
130
+	/**@brief IF state to RPN arguments transformation */
131
+	if2rpn_f if2rpn;
132
+	/**@brief RPN arguments to IF state transformation */
133
+	rpn2if_f rpn2if;
134
+
135
+};
136
+
137
+/**@brief Alloc a new @ref rpn_if_s from two transformation functions
138
+ * @param if2rpn informations about data to rpn args transformation
139
+ * @param rpn2if informations about rpn args to data transformation 
140
+ * @param rpn list of RPN expresions ( must be of rpn2if->argc size !!!)
141
+ * @return A pointer on an allocated @ref rpn_if_s
142
+ */
143
+rpn_if_t* rpn_if_new(size_t mem_sz, rpn_if_transfo_t *if2rpn,
144
+	rpn_if_transfo_t *rpn2if, rpn_expr_t *rpn);
145
+
146
+/**@brief Deallocate an @ref rpn_ifs_s and close associated @ref rpn_expr_s
147
+ * @param rif The IF to deallocate
148
+ */
149
+void rpn_if_free(rpn_if_t *rif);
150
+
151
+
152
+/**@brief Update all RPN expressions
153
+ * @param rif The concerned IF
154
+ * @param rpn A list of tokenized expressions (must be of rif->rpn_sz size)
155
+ * @return 0 if no error else -1
156
+ * @note Shortcut for @ref rpn_if_rpn_upd_rng(rif, rpns, rif->rpn_sz, 0);
157
+ */
158
+int rpn_if_rpn_upd(rpn_if_t *rif, rpn_tokenized_t *rpns);
159
+/**@brief Update a range of RPN expressions
160
+ * @param rif The concerned IF
161
+ * @param rpn A list of tokenized expressions
162
+ * @param sz Number of rpn expression in rpn argument
163
+ * @param offset Start updating expressions from this offset
164
+ * @return 0 if no error else -1
165
+ */
166
+int rpn_if_rpn_upd_rng(rpn_if_t *rif, rpn_tokenized_t *rpns, size_t sz,
167
+	size_t offset);
168
+
169
+/**@brief New @ref rpn_if_s and partial initialisation
170
+ * @param mem_sz memory size in bytes
171
+ * @param argc number of arguments taken by @ref rpn_expr_s
172
+ * @param rpn_count number of @ref rpn_expr_s
173
+ */
174
+rpn_if_t* _rpn_if_new(size_t mem_sz, size_t rpn_argc, size_t rpn_count,
175
+	size_t value_sz);
176
+
177
+/**@page doc_ifs
178
+ *
179
+ * Iterated functions system are a composed of Iterated function choosed
180
+ * randomly.
181
+ *
182
+ * @section doc_ifs_if Iterated function general considerations
183
+ *
184
+ * Iterated functions can be seen as transformations in a given space.
185
+ * Functions takes items as argument and set an item as a result.
186
+ *
187
+ * For the moment, the main goal is to interact with a 2 dimension world with
188
+ * 1 to 3 values per items (an image in grayscale or in RGB).
189
+ *
190
+ * A simple approach can be to use a single expression working with a single
191
+ * number later decomposed in multiple composant using bitmasks (basically
192
+ * for storage address and stored value).
193
+ *
194
+ * This can later be decomposed by assigning one (or multiple) expression
195
+ * to each composant (one expression for storage address, another one for
196
+ * the storage value).
197
+ *
198
+ * The same consideration can be done on argument number/composition taken
199
+ * by the expression.
200
+ *
201
+ * @subsection doc_ifs_if_io Iterated function generalisation
202
+ *
203
+ * A generic implementation can be IF as :
204
+ * - A generic input transformation system : X arguments transformed in Y
205
+ * RPNExpression arguments
206
+ * - A generic output system : N results (from N RPNExpr) transformed in X
207
+ * results
208
+ * - A generic transformation system : N expressions, taking Y arguments
209
+ *
210
+ * @section doc_ifs_if_api Iterated Function API
211
+ *
212
+ * Multiple steps are expected in API development :
213
+ * - A simple generic API will be defined (something like expecting a
214
+ * void* fun(void*) transforming X data in Y result using a blackbox optimized
215
+ * behavior associated with a memory map
216
+ * - Helper function will be written allowing C and/or Python extensions
217
+ */
218
+
219
+#endif
220
+

+ 41
- 0
rpn_ifs.h View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_ifs__h__
20
+#define __rpn_ifs__h__
21
+
22
+#include "config.h"
23
+
24
+#include "rpn_jit.h"
25
+
26
+/**@defgroup ifs Iterated function system
27
+ * @brief IFS implementation with RPN expressions
28
+ *
29
+ * IFS are basically a list of @ref ifs_if associated with a probability
30
+ * of evaluation.
31
+ *
32
+ * This implementation aims to :
33
+ * - optimize @ref ifs_if calls
34
+ * - optimize random number generation and IF random calls
35
+ * 
36
+ * @note It aims to provide an API close to @ref ifs_if API, in order to
37
+ * be able to use both IFS and IF almost tansparently via Python API.
38
+ */
39
+
40
+#endif
41
+

+ 465
- 0
rpn_jit.c View File

@@ -0,0 +1,465 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "rpn_jit.h"
20
+
21
+int rpn_expr_init(rpn_expr_t* expr, const unsigned char stack_sz,
22
+	  const size_t args_count)
23
+{
24
+#ifdef DEBUG
25
+	if(!expr)
26
+	{
27
+		dprintf(2, "Error, NULL ptr given as expression to rpn_expr_init");
28
+		errno = EINVAL;
29
+		return -1;
30
+	}
31
+#endif
32
+	bzero(expr, sizeof(rpn_expr_t));
33
+
34
+	expr->stack_sz = stack_sz;
35
+	expr->args_count = args_count;
36
+
37
+	expr->state = RPN_SOURCE;
38
+	memset(expr->err_reason, (int)'\0', 128);
39
+
40
+	expr->stack = malloc(sizeof(unsigned long) * stack_sz);
41
+	if(!expr->stack)
42
+	{
43
+		snprintf(expr->err_reason, 128,
44
+			"Unable to malloc stack : %s", strerror(errno));
45
+		expr->state = RPN_ERROR;
46
+		return -1;
47
+	}
48
+	bzero(expr->stack, sizeof(unsigned long) * stack_sz);
49
+
50
+	if(_rpn_expr_init_map(expr) < 0)
51
+	{
52
+		snprintf(expr->err_reason, 128, 
53
+			"Unable to init code map : %s", strerror(errno));
54
+		free(expr->expr);
55
+		expr->state = RPN_ERROR;
56
+		return -1;
57
+	}
58
+	return 0;
59
+}
60
+
61
+int rpn_expr_compile(rpn_expr_t *expr, const char *code)
62
+{
63
+#ifdef DEBUG
64
+	if(!expr)
65
+	{
66
+		dprintf(2, "Error, NULL ptr given as expression to rpn_expr_compile");
67
+		errno = EINVAL;
68
+		return -1;
69
+	}
70
+#endif
71
+	expr->expr = strdup(code);
72
+	if(!expr->expr)
73
+	{
74
+		snprintf(expr->err_reason, 128, 
75
+			"Unable to strdup expression : %s", strerror(errno));
76
+		expr->state = RPN_ERROR;
77
+		return -1;
78
+	}
79
+	return _rpn_expr_compile_expr(expr);
80
+}
81
+
82
+int rpn_expr_untokenize(rpn_expr_t *expr, rpn_tokenized_t *tokens, char long_op)
83
+{
84
+	int err;
85
+	size_t i;
86
+
87
+
88
+	errno = 0;
89
+	#ifdef DEBUG
90
+	if(!expr)
91
+	{
92
+		dprintf(2, "Error, NULL ptr given as expression to rpn_expr_untokenize");
93
+		err = EINVAL;
94
+		goto ret_err;
95
+	}
96
+	if(tokens->argc != expr->args_count)
97
+	{
98
+		/* even if it should work with tokens->argc < expr->args_count */
99
+		snprintf(expr->err_reason, 128,
100
+			"Expression argc differ from tokenized version");
101
+		err = EINVAL;
102
+		goto ret_err;
103
+	}
104
+	#endif
105
+	if(!(expr->expr = rpn_tokenized_expr(tokens, long_op)))
106
+	{
107
+		err = errno;
108
+		snprintf(expr->err_reason, 128,
109
+			"Error reading tokenized expression : %s",
110
+			strerror(err));
111
+		goto ret_err;
112
+	}
113
+
114
+	for(i=0; i<tokens->tokens_sz; i++)
115
+	{
116
+		if(_rpn_expr_token_copy(expr, &(tokens->tokens[i])) < 0)
117
+		{
118
+			err = errno;
119
+			if(errno == EINVAL)
120
+			{
121
+				dprintf(2,
122
+"Fatal error, unknown token type : %d.\nMemory corruption ?\n",
123
+					tokens->tokens[i].type);
124
+				exit(1);
125
+			}
126
+			snprintf(expr->err_reason, 128,
127
+				"Untokenize error : %s",
128
+				strerror(err));
129
+			goto ret_err;
130
+		}
131
+	}
132
+
133
+	return 0;
134
+
135
+	ret_err:
136
+		errno = err;
137
+		return -1;
138
+}
139
+
140
+char* rpn_random(size_t op_sz, size_t args_count)
141
+{
142
+	double step;
143
+	size_t i, buff_sz, offset, rnd;
144
+	char *buff, *cur;
145
+	unsigned char op_n;
146
+	unsigned long int seed, rnd_val;
147
+	int nchr, err;
148
+
149
+	buff_sz = offset = 0;
150
+	buff = NULL;
151
+	step = 1.0 / (rpn_op_sz() + (args_count>0?2:1)); // + args and values
152
+	
153
+	if(getrandom(&seed, sizeof(long int), 0) < 0)
154
+	{
155
+		err=errno;
156
+		perror("Fails to get a random number from kernel");
157
+		errno=err;
158
+		return NULL;
159
+	}
160
+	srand48(seed);
161
+
162
+	for(i=0; i<op_sz; i++)
163
+	{
164
+		if(buff_sz - offset < 21)
165
+		{
166
+			buff_sz += 40;
167
+			cur = realloc(buff, sizeof(char) * buff_sz);
168
+			if(!cur)
169
+			{
170
+				err=errno;
171
+				perror("Error allocating random expression");
172
+				errno=err;
173
+				return NULL;
174
+			}
175
+			buff=cur;
176
+		}
177
+		cur = buff + offset;
178
+		*cur = '\0';
179
+		op_n = drand48() / step;
180
+		if(op_n < rpn_op_sz())
181
+		{
182
+			cur[0] = rpn_ops[op_n].chr;
183
+			cur[1] = ' ';
184
+			cur[2] = '\0';
185
+			offset += 2;
186
+		}
187
+		else if(op_n == rpn_op_sz())
188
+		{
189
+
190
+			if(getrandom(&rnd_val, sizeof(long int), 0) < 0)
191
+			{
192
+				err=errno;
193
+				perror("Fails to get a random number for value");
194
+				errno=err;
195
+				return NULL;
196
+			}
197
+			// values
198
+			if((nchr = sprintf(cur, "0x%lX ", rnd_val)) < 0)
199
+			{
200
+				err=errno;
201
+				perror("Error while sprintf arguments in random generator");
202
+				errno=err;
203
+				return NULL;
204
+			}
205
+			offset += nchr;
206
+		}
207
+		else
208
+		{
209
+			rnd = drand48() / (1.0 / args_count);
210
+			// arguments
211
+			if((nchr = sprintf(cur, "A%ld ", rnd)) < 0)
212
+			{
213
+				err=errno;
214
+				perror("Error while sprintf arguments in random generator");
215
+				errno=err;
216
+				return NULL;
217
+			}
218
+			offset += nchr;
219
+		}
220
+	}
221
+	buff[offset] = '\0';
222
+	return buff;
223
+}
224
+
225
+int _rpn_expr_compile_expr(rpn_expr_t* expr)
226
+{
227
+	rpn_tokenizer_t tokenizer;
228
+	rpn_token_t *token;
229
+
230
+	
231
+	if(expr->state == RPN_ERROR)
232
+	{
233
+		goto err;
234
+	}
235
+
236
+	if(rpn_tokenizer_start(&tokenizer, &(expr->toks), expr->expr,
237
+		expr->args_count) < 0)
238
+	{
239
+		snprintf(expr->err_reason, 128,
240
+			"Error starting tokenizer : %s",
241
+			tokenizer.err_reason);
242
+		goto err;
243
+	}
244
+
245
+	while((token = rpn_tok(&tokenizer)))
246
+	{
247
+		if(_rpn_expr_token_copy(expr, token) < 0)
248
+		{
249
+			if(errno == EINVAL)
250
+			{
251
+				dprintf(2,
252
+"Fatal error, unknown token type : %d chr %ld.\nMemory corruption ?\n",
253
+					token->type, tokenizer.chr_no);
254
+				exit(1);
255
+			}
256
+			snprintf(expr->err_reason, 128,
257
+"Compilation error on chr %ld, unable to copy code part : %s",
258
+				tokenizer.chr_no, strerror(errno));
259
+			goto err;
260
+		}
261
+	}
262
+	if(rpn_tokenizer_error(&tokenizer))
263
+	{
264
+		snprintf(expr->err_reason, 128,
265
+			"Compilation error, chr %ld : %s",
266
+			tokenizer.chr_no, tokenizer.err_reason);
267
+		goto err;
268
+	}
269
+
270
+	if(_rpn_expr_end_map(expr))
271
+	{
272
+		snprintf(expr->err_reason, 128,
273
+			"Error ending code map : %s",
274
+			strerror(errno));
275
+		expr->state = RPN_ERROR;
276
+		return -1;
277
+	}
278
+
279
+	expr->state = RPN_READY;
280
+	return 0;
281
+	err:
282
+		expr->state = RPN_ERROR;
283
+		return -1;
284
+}
285
+
286
+int _rpn_expr_compile_tokens(rpn_expr_t* expr)
287
+{
288
+	size_t i;
289
+	rpn_token_t *token;
290
+	for(i=0; i<expr->toks.tokens_sz; i++)
291
+	{
292
+		token = &(expr->toks.tokens[i]);
293
+		if(_rpn_expr_token_copy(expr, token) < 0)
294
+		{
295
+			if(errno == EINVAL)
296
+			{
297
+				dprintf(2,
298
+"Fatal error, unknown token type : %d\nMemory corruption ?\n",
299
+					token->type);
300
+				exit(1);
301
+			}
302
+			snprintf(expr->err_reason, 128,
303
+			"Compilation error, unable to copy code part : %s",
304
+				strerror(errno));
305
+			expr->state = RPN_ERROR;
306
+			return -1;
307
+		}
308
+
309
+	}
310
+
311
+	if(_rpn_expr_end_map(expr))
312
+	{
313
+		snprintf(expr->err_reason, 128,
314
+			"Error ending code map : %s",
315
+			strerror(errno));
316
+		expr->state = RPN_ERROR;
317
+		return -1;
318
+	}
319
+
320
+	expr->state = RPN_READY;
321
+	return 0;
322
+}
323
+
324
+unsigned long rpn_expr_eval(rpn_expr_t *expr, unsigned long *args)
325
+{
326
+	rpn_run_f expr_run;
327
+	unsigned long int res;
328
+	if(expr->state == RPN_ERROR)
329
+	{
330
+		return 0;
331
+	}
332
+	expr_run = expr->code_map;
333
+	res = expr_run(expr->stack_sz, args, expr->stack);
334
+	return res;
335
+}
336
+
337
+void rpn_expr_close(rpn_expr_t* expr)
338
+{
339
+	if(expr->expr)
340
+	{
341
+		free(expr->expr);
342
+		expr->expr = NULL;
343
+	}
344
+	if(expr->stack)
345
+	{
346
+		free(expr->stack);
347
+		expr->stack = NULL;
348
+	}
349
+	if(expr->code_map)
350
+	{
351
+		if(munmap(expr->code_map, expr->code_map_sz))
352
+		{
353
+			perror("Unable to unmap code_map");
354
+		}
355
+		expr->code_map = NULL;
356
+	}
357
+}
358
+
359
+void rpn_expr_reset_stack(rpn_expr_t *expr)
360
+{
361
+	bzero(expr->stack, sizeof(unsigned long) * expr->stack_sz);
362
+}
363
+
364
+int _rpn_expr_token_copy(rpn_expr_t *expr, rpn_token_t *token)
365
+{
366
+	unsigned long int *value;
367
+	rpn_op_t local_op;
368
+	value = NULL;
369
+	switch(token->type)
370
+	{
371
+		case RPN_op:
372
+			local_op = *(token->op);
373
+			value = NULL;
374
+			break;
375
+		case RPN_arg:
376
+			local_op.fun = &rpn_arg;
377
+			local_op.fun_sz = &(CODE_SZ(rpn_arg));
378
+			value = &(token->arg_n);
379
+			break;
380
+		case RPN_val:
381
+			local_op.fun = &rpn_value;
382
+			local_op.fun_sz = &(CODE_SZ(rpn_value));
383
+			value = &(token->value);
384
+			break;
385
+		default:
386
+			errno = EINVAL;
387
+			return -1;
388
+	}
389
+	if(_rpn_code_part_cpy(expr, local_op.fun, *(local_op.fun_sz),
390
+		value))
391
+	{
392
+		return -1;
393
+	}
394
+	return 0;
395
+}
396
+
397
+int _rpn_code_part_cpy(rpn_expr_t *expr, const void *code_part,
398
+	unsigned long code_part_sz, const unsigned long *value)
399
+{
400
+	size_t old_sz, code_sz;
401
+	void *new_ptr;
402
+//printf("DEBUG _copy : %p %ld %p:%ld\n", code_part, code_part_sz, value, value?*value:0);
403
+	code_sz = expr->code_map_ptr - expr->code_map;
404
+	if(!expr->code_map_sz)
405
+	{
406
+		errno = EINVAL;
407
+		return -1;
408
+	}
409
+	if(code_sz + code_part_sz >= expr->code_map_sz)
410
+	{
411
+		old_sz = expr->code_map_sz;
412
+		expr->code_map_sz = (((code_sz + code_part_sz)>>9)+1)<<9;
413
+		new_ptr = mremap(expr->code_map, old_sz, expr->code_map_sz,
414
+			MREMAP_MAYMOVE);
415
+		if(new_ptr == (void*)-1)
416
+		{
417
+			expr->code_map_sz = 0;
418
+			return -1;
419
+		}
420
+		expr->code_map = new_ptr;
421
+		expr->code_map_ptr = expr->code_map + code_sz;
422
+	}
423
+	memcpy(expr->code_map_ptr, code_part, code_part_sz);
424
+	
425
+	if(value)
426
+	{
427
+		// set 1st instruction argument
428
+		*(unsigned long*)(expr->code_map_ptr + 2) = *value;
429
+	}
430
+
431
+	expr->code_map_ptr += code_part_sz;
432
+	return 0;
433
+}
434
+
435
+int _rpn_expr_init_map(rpn_expr_t* expr)
436
+{
437
+	expr->code_map_sz = RPN_MAP_CHUNK;
438
+	expr->code_map = mmap(NULL, expr->code_map_sz, PROT_READ | PROT_WRITE,
439
+		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
440
+	if(!expr->code_map)
441
+	{
442
+		return -1;
443
+	}
444
+	expr->code_map_ptr = expr->code_map;
445
+	if(CODE_PART_CPY(expr, rpn_exec))
446
+	{
447
+		return -1;
448
+	}
449
+	return 0;
450
+}
451
+
452
+int _rpn_expr_end_map(rpn_expr_t *expr)
453
+{
454
+	if(CODE_PART_CPY(expr, rpn_exec_ret))
455
+	{
456
+		return -1;
457
+	}
458
+	if(mprotect(expr->code_map, expr->code_map_ptr - expr->code_map,
459
+		PROT_EXEC))
460
+	{
461
+		return -1;
462
+	}
463
+	return 0;
464
+}
465
+

+ 251
- 0
rpn_jit.h View File

@@ -0,0 +1,251 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_jit__h__
20
+#define __rpn_jit__h__
21
+
22
+#include "config.h"
23
+
24
+#include <stdlib.h>
25
+#include <errno.h>
26
+#include <stdio.h>
27
+#include <string.h>
28
+#include <sys/mman.h>
29
+#include <sys/random.h>
30
+
31
+
32
+#include "rpn_lib.h"
33
+#include "rpn_parse.h"
34
+
35
+/**@defgroup rpn RPN evaluation lib
36
+ * @brief High speed RPN expression evaluation librarie
37
+ */
38
+/**@defgroup rpn_compile RPN expression compilation
39
+ * @brief How is an RPN expression transformed into a callable function
40
+ *
41
+ * Compilation is done in two steps :
42
+ * - @ref rpn_tokenize 
43
+ * - @ref rpn_exec
44
+ * @ingroup rpn
45
+ */
46
+/**@defgroup rpn_cmap Executable code map creation
47
+ * @brief A code map containing compiled code to evaluate the expression
48
+ * 
49
+ * In order to provide a callable memory adress, the compilation process starts
50
+ * by creating a new memory map. Then @ref rpn_lib precompiled symbols are
51
+ * copyied in it, suffixed by @ref rpn_exec_ret symbol. Finally, the map is set executable using mprotect.
52
+ * @ingroup rpn_compile
53
+ */
54
+
55
+/**@file rpn_jit.h
56
+ * @brief Contains structure & functions for rpn_expr_t manipulation & evaluation
57
+ * @ingroup rpn
58
+ */
59
+
60
+/**@brief Mremap chunk size */
61
+#define RPN_MAP_CHUNK 512
62
+
63
+/**@brief @ref rpn_expr_t error state */
64
+#define RPN_ERROR -1
65
+/**@brief @ref rpn_expr_t initialization state */
66
+#define RPN_SOURCE 0
67
+/**@brief @ref rpn_expr_t ready to eval state */
68
+#define RPN_READY 1
69
+
70
+/**@brief Code part copy macro
71
+ * @param expr rpn_expr_t* A pointer on @ref rpn_expr_t
72
+ * @param NAME code part name
73
+ * @see _rpn_code_part_cpy
74
+ * @ingroup rpn_cmap
75
+ */
76
+#define CODE_PART_CPY(expr, NAME) _rpn_code_part_cpy(expr, &NAME, CODE_SZ(NAME), NULL)
77
+
78
+/**@brief Copy a value push in rpn code ptr
79
+ * @param expr rpn_expr_t* A pointer on @ref rpn_expr_t
80
+ * @param value unsigned long that will be push
81
+ * @see _rpn_code_part_cpy
82
+ * @ingroup rpn_cmap
83
+ */
84
+#define CODE_VALUE_CPY(expr, value) _rpn_code_part_cpy(expr, &rpn_value, CODE_SZ(rpn_value), &(value));
85
+
86
+/**@brief Add an instruction to push an argument on RPN stack
87
+ * @param expr rpn_expr_t* A pointer on @ref rpn_expr_t
88
+ * @param value unsigned long The argument number
89
+ * @see _rpn_code_part_cpy
90
+ * @ingroup rpn_cmap
91
+ */
92
+#define CODE_ARG_CPY(expr, value) _rpn_code_part_cpy(expr, &rpn_arg, CODE_SZ(rpn_arg), &(value));
93
+
94
+/**@brief RPN evaluation function type
95
+ * @ingroup rpn_cmap
96
+ */
97
+typedef unsigned long (*rpn_run_f)(unsigned long, unsigned long*, void*);
98
+
99
+/**@brief RPN expression type
100
+ * @ingroup rpn */
101
+typedef struct rpn_expr_s rpn_expr_t;
102
+
103
+/**@brief Stores RPN expression informations 
104
+ * @ingroup rpn
105
+ */
106
+struct rpn_expr_s
107
+{
108
+	/**@brief pointer on RPN expression */
109
+	char *expr;
110
+	/**@brief Tokenized version of expression (set by @ref rpn_expr_compile)*/
111
+	rpn_tokenized_t toks;
112
+
113
+	/**@brief pointer on jit code memory map */
114
+	void *code_map;
115
+
116
+	/**@brief Pointer on next copy addr in code map */
117
+	void *code_map_ptr;
118
+	/**@brief Code map size */
119
+	size_t code_map_sz;
120
+
121
+	/**@brief rpn expression arguments count */
122
+	size_t args_count;
123
+
124
+	/**@brief Stack values memory */
125
+	unsigned long *stack;
126
+	/**@brief rpn jit function stack size */
127
+	unsigned char stack_sz;
128
+
129
+	/**@brief Expression status. Takes value in @ref RPN_ERROR,
130
+	 * @ref RPN_SOURCE, @ref RPN_READY */
131
+	short state;
132
+	/**@brief Error reason */
133
+	char err_reason[128];
134
+};
135
+
136
+/**@brief Initialize a new @ref rpn_expr_s
137
+ * @param expr Pointer on the expression
138
+ * @param stack_sz Expression stack size
139
+ * @param args_count Expression argument counter
140
+ * @return 0 if no error else -1
141
+ * @note Once rpn_expr_init has been called, you have to call
142
+ * @ref rpn_expr_close in order to free handled ressources
143
+ * @ingroup rpn
144
+ */
145
+int rpn_expr_init(rpn_expr_t* expr, const unsigned char stack_sz,
146
+	  const size_t args_count);
147
+
148
+/**@brief Starts a new initialized expression from an expression string
149
+ * @param expr Pointer on an intialized expression ( see @ref rpn_expr_init )
150
+ * @param code '\0' terminated string representing the RPN expr
151
+ * @return 0 if no error else -1 and @ref rpn_expr_s::err_reason is set
152
+ * @note The expression is copied, given buffer can be deallocated after call
153
+ * @ingroup rpn
154
+ */
155
+int rpn_expr_compile(rpn_expr_t *expr, const char *code);
156
+
157
+/**@brief Starts a new initialized expression from a tokenized form
158
+ * @param expr Pointer on an intialized expression ( see @ref rpn_expr_init )
159
+ * @param tokens Tokenized form
160
+ * @param long_op If true long operation are used, else 1 chr op are used
161
+ * @return 0 if no error else -1 and @ref rpn_expr_s::err_reason is set
162
+ * @note The @ref rpn_tokenized_s::tokens attribute is "captured" by the
163
+ * @ref rpn_expr_s structure and will be deallocated when @ref rpn_expr_close
164
+ * is called. It is not encouraged to keep a reference on this attribute after
165
+ * @ref rpn_expr_start_untokenize call (but pointed @ref rpn_tokenized_t 
166
+ * can be reused)
167
+ * @ingroup rpn
168
+ */
169
+int rpn_expr_untokenize(rpn_expr_t *expr, rpn_tokenized_t *tokens, char long_op);
170
+
171
+/**@brief Generate a new random rpn_expression
172
+ * @param op_sz Number of token in generated expression
173
+ * @param args_count Number of arguments for generated expression
174
+ * @return A pointer on allocated memory storing a valid random RPN expression.
175
+ * NULL if error with errno set (produce a message on stderr)
176
+ * @ingroup rpn
177
+ */
178
+char* rpn_random(size_t op_sz, size_t args_count);
179
+
180
+/**@brief Compile an new RPN expression from string expression
181
+ * @param expr Pointer on @ref rpn_expr_s
182
+ * @return 0 if no error else -1 and set @ref rpn_expr_s err_reason
183
+ * @ingroup rpn_compile
184
+ */
185
+int _rpn_expr_compile_expr(rpn_expr_t* expr);
186
+/**@brief Compile an new RPN expression from string expression
187
+ * @param expr Pointer on @ref rpn_expr_s
188
+ * @return 0 if no error else -1 and set @ref rpn_expr_s err_reason
189
+ * @ingroup rpn_compile
190
+ */
191
+int _rpn_expr_compile_tokens(rpn_expr_t* expr);
192
+
193
+/**@brief Evaluate an RPN expression
194
+ * @param expr Pointer on @ref rpn_expr_s
195
+ * @param args List of arguments
196
+ * @return Head of stack after evaluation
197
+ * @note If expression not compiled yet compile it before eval
198
+ * @ingroup rpn
199
+ */
200
+unsigned long rpn_expr_eval(rpn_expr_t *expr, unsigned long *args);
201
+
202
+/**@brief Free ressources handled by given @ref rpn_expr_s
203
+ * @param expr Pointer on rpn_expr_t
204
+ * @param expr Pointer on @ref rpn_expr_s
205
+ * @ingroup rpn
206
+ */
207
+void rpn_expr_close(rpn_expr_t* expr);
208
+
209
+/**@brief bzero memory allocated for stack
210
+ * @param expr Pointer on rpn_expr_t
211
+ * @ingroup rpn
212
+ */
213
+void rpn_expr_reset_stack(rpn_expr_t *expr);
214
+
215
+/**@brief Copy precompiled code from @ref rpn_lib.h in expression code map
216
+ * @param expr The expression being compiled
217
+ * @param token Pointer on token informations
218
+ * @return 0 if no error else -1, set error and errno
219
+ * @ingroup rpn_cmap
220
+ */
221
+int _rpn_expr_token_copy(rpn_expr_t *expr, rpn_token_t *token);
222
+
223
+/**@brief Copy a part of code to the code map
224
+ * @warning designed to be use with @ref CODE_PART_CPY and CODE_VALUE_CPY macros
225
+ * @param expr Pointer on rpn_expr_t
226
+ * @param code_part copy src address
227
+ * @param code_part_sz the code part size
228
+ * @param value if not NULL set the first code part
229
+ * 	instruction argument to this value
230
+ * @return 0 if no error else -1 and set errno
231
+ * @ingroup rpn_cmap
232
+ */
233
+int _rpn_code_part_cpy(rpn_expr_t* expr, const void* code_part,
234
+	unsigned long code_part_sz, const unsigned long *value);
235
+
236
+/**@brief Allocate a memory map for given rpn_expt_t and copy the function
237
+ * header
238
+ * @param expr Pointer on rpn_expr_t
239
+ * @return 0 if no error, else -1 and set errno
240
+ * @ingroup rpn_cmap
241
+ */
242
+int _rpn_expr_init_map(rpn_expr_t* expr);
243
+
244
+/**@brief Copy the function suffix and change memory map protection
245
+ * @param expr Pointer on rpn_expr_t
246
+ * @return 0 if no error, else -1 and set errno
247
+ * @ingroup rpn_cmap
248
+ */
249
+int _rpn_expr_end_map(rpn_expr_t *expr);
250
+
251
+#endif

+ 246
- 0
rpn_lib.asm View File

@@ -0,0 +1,246 @@
1
+; TODO Use memory area given in argument to preserve stack between calls
2
+[bits 64]
3
+
4
+; local variables macro
5
+%define stack_size [rbp - 8]
6
+%define stack_base [rbp - 16]
7
+%define brkinit [rbp - 24]
8
+%define args_ptr [rbp - 32]
9
+
10
+%define stack_cur r11
11
+
12
+; push & pop the RPN stack macros
13
+; check if stack full, then push value
14
+%macro rpn_push 1
15
+	inc stack_cur
16
+	cmp stack_cur, stack_size
17
+	jl %%end
18
+	xor stack_cur, stack_cur
19
+	%%end:
20
+	mov [r10 + stack_cur * 8], %1
21
+%endmacro
22
+
23
+; check if stack empty, then pop value
24
+%macro rpn_pop 1
25
+	mov %1, [r10 + stack_cur * 8]
26
+	cmp stack_cur, 0
27
+	jg %%end
28
+	mov stack_cur, stack_size
29
+	%%end:
30
+	dec stack_cur
31
+%endmacro
32
+
33
+; define a label with code portion size
34
+; and set global
35
+%macro part_sz 1
36
+	%1_sz: dq $ - %1
37
+	global %1
38
+	global %1_sz
39
+%endmacro
40
+
41
+section .data
42
+
43
+rpn_exec:
44
+	; unsigned long int rpn_exec(unsigned long int stack_size, unsigned long args)
45
+	; rdi -> stack size (in values (64 bits))
46
+	; rsi -> args pointers
47
+	
48
+	enter 32, 0
49
+	
50
+	mov stack_size, rdi
51
+	mov args_ptr, rsi
52
+	mov r10, rdx ; r10 is stack base
53
+
54
+	xor rdx, rdx
55
+	mov brkinit, rdx
56
+
57
+	cmp r10, 0
58
+	jne .nobrk
59
+	; if stack is NULL allocate using brk
60
+
61
+	mov rax, 0x0C
62
+	xor rdi, rdi ; get brk
63
+	syscall
64
+
65
+	mov brkinit, rax
66
+	mov rdi, stack_size
67
+	shl rdi, 3 ; mul 8
68
+	add rdi, rax
69
+	mov rax, 0x0C
70
+	syscall ; new brk
71
+
72
+	cmp rax, -1
73
+	jne .cont
74
+	leave
75
+	ret
76
+	.cont:
77
+
78
+	mov rcx, stack_size
79
+	mov rdi, brkinit
80
+	xor rax, rax
81
+	.bzero:
82
+		stosq
83
+		loop .bzero
84
+	mov r10, brkinit
85
+
86
+	.nobrk:
87
+	xor stack_cur, stack_cur ; r11 is stack item counter
88
+	
89
+
90
+part_sz rpn_exec
91
+
92
+rpn_exec_ret:
93
+	rpn_pop rax
94
+	mov rdi, brkinit
95
+	cmp rdi, 0
96
+	jne .end
97
+	push rax
98
+	mov rax, 0x0C
99
+	mov rdi, brkinit
100
+	syscall
101
+	pop rax
102
+	.end:
103
+	leave
104
+	ret
105
+part_sz rpn_exec_ret
106
+
107
+rpn_value:
108
+	mov rax, strict qword 0x12345678
109
+	rpn_push rax
110
+part_sz rpn_value
111
+
112
+rpn_arg:
113
+	mov rax, strict qword 0x12345678
114
+	mov rsi, 8
115
+	mul rsi
116
+	mov rsi, args_ptr
117
+	add rsi, rax
118
+	mov rax, [rsi]
119
+	rpn_push rax
120
+part_sz rpn_arg
121
+
122
+rpn_add:
123
+	rpn_pop rax
124
+	rpn_pop rbx
125
+	add rax, rbx
126
+	rpn_push rax
127
+part_sz rpn_add
128
+
129
+rpn_sub:
130
+	rpn_pop rbx
131
+	rpn_pop rax
132
+	sub rax, rbx
133
+	rpn_push rax
134
+part_sz rpn_sub
135
+
136
+rpn_mul:
137
+	rpn_pop rbx
138
+	rpn_pop rax
139
+	mul rbx
140
+	rpn_push rax
141
+part_sz rpn_mul
142
+
143
+rpn_div:
144
+	rpn_pop rbx
145
+	rpn_pop rax
146
+	xor rdx, rdx
147
+	test rbx, rbx
148
+	jz .zerodiv
149
+	div rbx
150
+	jmp .end
151
+	.zerodiv: xor rax, rax
152
+	.end:
153
+	rpn_push rax
154
+part_sz rpn_div
155
+
156
+rpn_mod:
157
+	rpn_pop rbx
158
+	rpn_pop rax
159
+	xor rdx, rdx
160
+	test rbx, rbx
161
+	jz .zerodiv
162
+	div rbx
163
+	jmp .end
164
+	.zerodiv: xor rax, rax
165
+	.end
166
+	rpn_push rdx
167
+part_sz rpn_mod
168
+
169
+rpn_neg:
170
+	rpn_pop rax
171
+	neg rax
172
+	rpn_push rax
173
+part_sz rpn_neg
174
+
175
+rpn_not:
176
+	rpn_pop rax
177
+	not rax
178
+	rpn_push rax
179
+part_sz rpn_not
180
+
181
+rpn_and:
182
+	rpn_pop rbx
183
+	rpn_pop rax
184
+	and rax, rbx
185
+	rpn_push rax
186
+part_sz rpn_and
187
+
188
+rpn_or:
189
+	rpn_pop rbx
190
+	rpn_pop rax
191
+	or rax, rbx
192
+	rpn_push rax
193
+part_sz rpn_or
194
+
195
+rpn_xor:
196
+	rpn_pop rbx
197
+	rpn_pop rax
198
+	xor rax, rbx
199
+	rpn_push rax
200
+part_sz rpn_xor
201
+
202
+rpn_shl:
203
+	rpn_pop rcx
204
+	rpn_pop rax
205
+	cmp rcx, 64
206
+	jge .zero
207
+	shl rax, cl
208
+	jmp .end
209
+	.zero:
210
+	xor rax, rax
211
+	.end:
212
+	rpn_push rax
213
+part_sz rpn_shl
214
+
215
+rpn_shr:
216
+	rpn_pop rcx
217
+	rpn_pop rax
218
+	cmp rcx, 64
219
+	jge .zero
220
+	shr rax, cl
221
+	jmp .end
222
+	.zero:
223
+	xor rax, rax
224
+	.end:
225
+	rpn_push rax
226
+part_sz rpn_shr
227
+
228
+rpn_xchg:
229
+	rpn_pop rbx
230
+	rpn_pop rax
231
+	rpn_push rbx
232
+	rpn_push rax
233
+part_sz rpn_xchg
234
+
235
+rpn_dup:
236
+	rpn_pop rax
237
+	rpn_push rax
238
+	rpn_push rax
239
+part_sz rpn_dup
240
+
241
+rpn_pop_op:
242
+	rpn_pop rax
243
+part_sz rpn_pop_op
244
+
245
+section .text
246
+

+ 122
- 0
rpn_lib.h View File

@@ -0,0 +1,122 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_lib__h__
20
+#define __rpn_lib__h__
21
+
22
+#include "config.h"
23
+
24
+#include <sys/mman.h>
25
+
26
+/**@defgroup rpn_lib RPNlib
27
+ * @brief Precompiled x86_64 assembly operations
28
+ * @ingroup rpn_compile
29
+ */
30
+
31
+/**@file rpn_lib.h
32
+ * @brief "Import" symbols defined in @ref rpn_lib.asm using @ref CODE_PART macro.
33
+ *
34
+ * For each symbol (piece of compiled assembly code) @ref CODE_PART defines :
35
+ * - void *NAME : the pointer on compiled code
36
+ * - unsigned long NAME_sz : the size of the code portion
37
+ *
38
+ * @see rpn_lib.asm
39
+ * @ingroup rpn_lib
40
+ */
41
+/**@file rpn_lib.asm
42
+ * @brief x86_64 RPN expression operations implementations
43
+ * 
44
+ * Exported symbols are found in @ref rpn_lib.h
45
+ * @ingroup rpn_lib
46
+ */
47
+
48
+
49
+/**@brief Code part size macro */
50
+#define CODE_SZ(NAME) NAME ## _sz
51
+
52
+/**@brief macro to declare a code part and associated size */
53
+#define CODE_PART(NAME) const extern void* NAME; extern const unsigned long NAME ## _sz
54
+
55
+/**@brief Function heading code
56
+ *
57
+ * - stack frame creation
58
+ * - argument processing
59
+ * - etc.
60
+ * @ingroup rpn_lib */
61
+CODE_PART(rpn_exec);
62
+/**@brief Function ends and ret code
63
+ *
64
+ * - pop and return head of stack value
65
+ * - stack frame deletion
66
+ * @ingroup rpn_lib */
67
+CODE_PART(rpn_exec_ret);
68
+/**@brief Constant value pushing symbol
69
+ * @note except a value to be set
70
+ * @ingroup rpn_lib */
71
+CODE_PART(rpn_value);
72
+/**@brief Argument pushing symbol
73
+ * @note except a value to be set
74
+ * @ingroup rpn_lib */
75
+CODE_PART(rpn_arg);
76
+/**@brief Addition symbol
77
+ * @ingroup rpn_lib */
78
+CODE_PART(rpn_add);
79
+/**@brief Substraction symbol
80
+ * @ingroup rpn_lib */
81
+CODE_PART(rpn_sub);
82
+/**@brief Division symbol
83
+ * @ingroup rpn_lib */
84
+CODE_PART(rpn_div);
85
+/**@brief Multiplication symbol
86
+ * @ingroup rpn_lib */
87
+CODE_PART(rpn_mul);
88
+/**@brief Modulo symbol
89
+ * @ingroup rpn_lib */
90
+CODE_PART(rpn_mod);
91
+/**@brief Left shift symbol
92
+ * @ingroup rpn_lib */
93
+CODE_PART(rpn_shl);
94
+/**@brief Right shift symbol
95
+ * @ingroup rpn_lib */
96
+CODE_PART(rpn_shr);
97
+/**@brief Value exchange symbol
98
+ * @ingroup rpn_lib */
99
+CODE_PART(rpn_xchg);
100
+/**@brief Head of stack duplication
101
+ * @ingroup rpn_lib */
102
+CODE_PART(rpn_dup);
103
+/**@brief Arithmetic negation
104
+ * @ingroup rpn_lib */
105
+CODE_PART(rpn_neg);
106
+/**@brief Binary not
107
+ * @ingroup rpn_lib */
108
+CODE_PART(rpn_not);
109
+/**@brief Binary and
110
+ * @ingroup rpn_lib */
111
+CODE_PART(rpn_and);
112
+/**@brief Binary or
113
+ * @ingroup rpn_lib */
114
+CODE_PART(rpn_or);
115
+/**@brief Binary xor
116
+ * @ingroup rpn_lib */
117
+CODE_PART(rpn_xor);
118
+/**@brief Pop head of stack
119
+ * @ingroup rpn_lib */
120
+CODE_PART(rpn_pop_op);
121
+
122
+#endif

+ 58
- 0
rpn_mutation.c View File

@@ -0,0 +1,58 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "rpn_mutation.h"
20
+
21
+/**@file rpn_mutation.c
22
+ * @todo continue implementation */
23
+
24
+const rpn_mutation_profile_t rpn_default_mutprof = {16,
25
+	{	RPN_del, RPN_del,
26
+		RPN_add, RPN_add,
27
+		RPN_chg, RPN_chg, RPN_chg, RPN_chg, RPN_chg, RPN_chg,
28
+		RPN_upd, RPN_upd, RPN_upd, RPN_upd, RPN_upd, RPN_upd}
29
+};
30
+
31
+rpn_expr_t* rpn_expr_mutation(rpn_expr_t *src, size_t mutations)
32
+{
33
+	return rpn_expr_mutation_p(src, mutations, &rpn_default_mutprof);
34
+}
35
+
36
+rpn_expr_t* rpn_expr_mutation_p(rpn_expr_t *src, size_t mutations,
37
+		const rpn_mutation_profile_t *prof)
38
+{
39
+	unsigned char op;
40
+	
41
+	prof = prof?prof:&rpn_default_mutprof;
42
+
43
+	op = prof->mods[(int)(drand48() / (1.0 / prof->mods_sz))];
44
+	switch(op)
45
+	{
46
+		case 0: // add a token
47
+			break;
48
+		case 1: // delete a token
49
+			break;
50
+		case 2: // update token type
51
+			break;
52
+		default: // update token, same type
53
+			break;
54
+	}
55
+	return NULL;
56
+}
57
+
58
+

+ 83
- 0
rpn_mutation.h View File

@@ -0,0 +1,83 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_mutation__h__
20
+#define __rpn_mutation__h__
21
+
22
+#include <stddef.h>
23
+
24
+#include "rpn_jit.h"
25
+
26
+/**@defgroup mutation RPN expression mutation
27
+ * @ingroup rpn
28
+ */
29
+/**@file rpn_mutation.h
30
+ * @brief Contains structures and function to mutate RPN epxressions
31
+ */
32
+
33
+
34
+/**@brief Defines mutation actions types */
35
+enum rpn_mutation_op_e {
36
+	/**@brief Mutation action : delete a token */
37
+	RPN_del,
38
+	/**@brief Mutation action : add a token */
39
+	RPN_add,
40
+	/**@brief Mutation action : change a token */
41
+	RPN_chg,
42
+	/**@brief Mutation action : update a token (same type, different value) */
43
+	RPN_upd
44
+};
45
+
46
+/**@brief Shortcut for struct @ref rpn_mutation_profile_s */
47
+typedef struct rpn_mutation_profile_s rpn_mutation_profile_t;
48
+
49
+/**@brief Stores mutation informations
50
+ * @ingroup mutation
51
+ */
52
+struct rpn_mutation_profile_s
53
+{
54
+	/**@brief Size of @ref rpn_mutation_profile_s::mods attribute */
55
+	size_t mods_sz;
56
+
57
+	/**@brief Modification possibilities
58
+	 *
59
+	 * One value is picked up randomly from this list to determine
60
+	 * the type of mutation : addition, deletion, modification, value change
61
+	 */
62
+	unsigned char mods[];
63
+};
64
+
65
+/**@brief Default mutation profile */
66
+extern const rpn_mutation_profile_t rpn_default_mutprof;
67
+
68
+/**@brief Shortcut for @ref rpn_expr_mutation_p with a @ref rpn_default_mutprof
69
+ * @ingroup mutation */
70
+rpn_expr_t* rpn_expr_mutation(rpn_expr_t *src, size_t mutations);
71
+
72
+/**@brief Generate a new expression by applying mutations to a source
73
+ * expression
74
+ * @param src Source expression
75
+ * @param mutations number of mutations
76
+ * @param prof Mutation profile
77
+ * @return A new instance of rpn_expr_t ready to be evaluate
78
+ * @ingroup mutation
79
+ */
80
+rpn_expr_t* rpn_expr_mutation_p(rpn_expr_t *src, size_t mutations,
81
+	const rpn_mutation_profile_t *prof);
82
+
83
+#endif

+ 387
- 0
rpn_parse.c View File

@@ -0,0 +1,387 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "rpn_parse.h"
20
+
21
+/**@brief Macro for @ref rpn_ops member definition
22
+ * @param NAME @ref rpn_lib.h symbol
23
+ * @param s The short (1 char) symbol code
24
+ * @param l The long (char*) symbole code
25
+ */
26
+#define __op(NAME, s, l) {&NAME, &CODE_SZ(NAME), s, l}
27
+const rpn_op_t rpn_ops[] = {\
28
+	__op(rpn_add, '+', "add"),\
29
+	__op(rpn_sub, '-', "sub"),\
30
+	__op(rpn_div, '/', "div"),\
31
+	__op(rpn_mul, '*', "mul"),\
32
+	__op(rpn_mod, '%', "mod"),\
33
+	__op(rpn_neg, '!', "neg"),\
34
+	__op(rpn_not, '~', "not"),\
35
+	__op(rpn_and, '&', "and"),\
36
+	__op(rpn_or, '|', "or"),\
37
+	__op(rpn_xor, '^', "xor"),\
38
+	__op(rpn_shr, 'r', ">>"),\
39
+	__op(rpn_shl, 'l', "<<"),\
40
+	__op(rpn_xchg, 'x', "xchg"),\
41
+	__op(rpn_dup, 'd', "dup"),\
42
+	__op(rpn_pop_op, 'p', "pop"),\
43
+};
44
+#undef __op
45
+
46
+int rpn_tokenizer_start(rpn_tokenizer_t *tokenizer, rpn_tokenized_t *dst,
47
+	const char* expr, size_t argc)
48
+{
49
+	int err;
50
+
51
+	bzero(tokenizer, sizeof(rpn_tokenizer_t));
52
+	tokenizer->orig = expr;
53
+
54
+	tokenizer->toks = dst;
55
+	tokenizer->toks->argc = argc;
56
+	tokenizer->toks->tokens_sz = 0;
57
+	tokenizer->toks->tokens = NULL;
58
+
59
+
60
+	if(!(tokenizer->buff = strdup(expr)))
61
+	{
62
+		err = errno;
63
+		snprintf(tokenizer->err_reason, 64,
64
+			"Error duplicating expression for tokenization : %s",
65
+			strerror(err));
66
+		errno = err;
67
+		return -1;
68
+	}
69
+	tokenizer->cur = tokenizer->buff;
70
+
71
+	return 0;
72
+}
73
+
74
+rpn_token_t* rpn_tok(rpn_tokenizer_t *tokenizer)
75
+{
76
+	char *token;
77
+	rpn_token_t ret, *tmp, *res;
78
+	int err;
79
+
80
+	token = tokenizer->cur;
81
+
82
+	if(!*token) // end of expression
83
+	{
84
+		rpn_tokenizer_free(tokenizer);
85
+		return NULL;
86
+	}
87
+
88
+	while(*(tokenizer->cur))
89
+	{
90
+		if(*(tokenizer->cur) == ' ' ||
91
+		   *(tokenizer->cur) == '\n' ||
92
+		   *(tokenizer->cur) == '\t')
93
+		{
94
+			break;
95
+		}
96
+		tokenizer->cur++;
97
+		tokenizer->chr_no++;
98
+	}
99
+	if(*(tokenizer->cur))
100
+	{
101
+		// not end, go on next chr for further rpn_tok calls
102
+		*(tokenizer->cur) = '\0';
103
+		tokenizer->cur++;
104
+		tokenizer->chr_no++;
105
+	}
106
+
107
+	// we have a clean token '\0' terminated
108
+	if(rpn_tokenize(token, &ret, tokenizer->err_reason) < 0)
109
+	{
110
+		errno = 0;
111
+		return NULL;
112
+	}
113
+	if(ret.type == RPN_arg && ret.arg_n >= tokenizer->toks->argc)
114
+	{
115
+		// invalid argument number
116
+		if(tokenizer->toks->argc)
117
+		{
118
+			snprintf(tokenizer->err_reason, 64,
119
+"Argument number is too big : should be in [0..%ld] but \"%s\" found",
120
+				tokenizer->toks->argc - 1, token);
121
+		}
122
+		else
123
+		{
124
+			snprintf(tokenizer->err_reason, 64,
125
+				"No argument accepted but \"%s\" found",
126
+				token);
127
+		}
128
+		errno = EINVAL;
129
+		return NULL;
130
+	}
131
+
132
+	if(tokenizer->toks->tokens_sz >= tokenizer->allocated_toks)
133
+	{
134
+		tokenizer->allocated_toks += 8;
135
+		tmp = realloc(tokenizer->toks->tokens,
136
+			sizeof(rpn_token_t) * tokenizer->allocated_toks);
137
+		if(!tmp)
138
+		{
139
+			err = errno;
140
+			snprintf(tokenizer->err_reason, 64,
141
+				"Unable to realloc tokens list : %s",
142
+				strerror(err));
143
+			errno = err;
144
+			return NULL;
145
+		}
146
+		tokenizer->toks->tokens = tmp;
147
+	}
148
+
149
+	res = &(tokenizer->toks->tokens[tokenizer->toks->tokens_sz]);
150
+	*res = ret;
151
+	tokenizer->toks->tokens_sz++;
152
+	return res;
153
+}
154
+
155
+int rpn_tokenize(const char *token, rpn_token_t *dst, char error[64])
156
+{
157
+	int rep;
158
+	unsigned long num;
159
+	const char *orig;
160
+
161
+	if((rep = rpn_match_token_i(token)) >= 0)
162
+	{
163
+		dst->type = RPN_op;
164
+		dst->op_n = rep;
165
+		dst->op = &(rpn_ops[rep]);
166
+		return 0;
167
+	}
168
+
169
+	orig = token;
170
+
171
+	if(*token == 'A')
172
+	{
173
+		if(!token[1])
174
+		{
175
+			snprintf(error, 64,
176
+				"Argument number is missing, lonely \"A \" found");
177
+			return -1;
178
+		}
179
+		dst->type = RPN_arg;
180
+		token++;
181
+	}
182
+	else
183
+	{
184
+		dst->type = RPN_val;
185
+	}
186
+
187
+	if(rpn_match_number(token, &num) < 0)
188
+	{
189
+		snprintf(error, 64,
190
+			"Invalid %snumber : \"%s\"",
191
+			dst->type == RPN_arg?"argument ":"constant ",
192
+			orig);
193
+		return -1;
194
+	}
195
+	
196
+	if(dst->type == RPN_arg)
197
+	{
198
+		dst->arg_n = num;
199
+	}
200
+	else
201
+	{
202
+		dst->value = num;
203
+	}
204
+	return 0;
205
+}
206
+
207
+void rpn_tokenizer_free(rpn_tokenizer_t *tokenizer)
208
+{
209
+	if(tokenizer->buff)
210
+	{
211
+		free(tokenizer->buff);
212
+		bzero(tokenizer, sizeof(rpn_tokenizer_t));
213
+	}
214
+}
215
+
216
+char* rpn_tokenized_expr(rpn_tokenized_t *tokens, char long_op)
217
+{
218
+	size_t expr_sz, i;
219
+	int err, written;
220
+	char *cur, *tmp, *expr;
221
+	rpn_token_t *token;
222
+	int ALLOC_CHUNK=22; /* 64 bit uint is 20 decimal digit */
223
+
224
+#ifdef DEBUG
225
+	if(!tokens)
226
+	{
227
+		dprintf(2, "Error, NULL ptr given to rpn_rokenize_expr");
228
+		err = EINVAL;
229
+		goto ret_err;
230
+	}
231
+#endif
232
+	errno = 0;
233
+	err = 0;
234
+	expr_sz = 0;
235
+	expr_sz = 128;
236
+	cur = expr = malloc(sizeof(char)*expr_sz);
237
+	if(!expr)
238
+	{
239
+		err=errno;
240
+		dprintf(2,"Error allocating memory for expression : %s",
241
+			strerror(err));
242
+		goto ret_err;
243
+	}
244
+	for(i=0; i<tokens->tokens_sz; i++)
245
+	{
246
+		token = &(tokens->tokens[i]);
247
+		if(cur - expr >= expr_sz - ALLOC_CHUNK)
248
+		{
249
+			expr_sz += 128;
250
+			tmp = realloc(expr, sizeof(char) * expr_sz);
251
+			if(!tmp)
252
+			{
253
+				err=errno;
254
+				dprintf(2,"Error allocating memory for expression : %s",
255
+					strerror(err));
256
+				goto ret_err;
257
+			}
258
+			cur = tmp + (cur - expr);
259
+			expr = tmp;
260
+		}
261
+		switch(token->type)
262
+		{
263
+			case RPN_op:
264
+				if(!long_op)
265
+				{
266
+					*cur = token->op->chr;
267
+					cur[1] = ' ';
268
+					cur[2] = '\0';
269
+					written = 2;
270
+				}
271
+				else
272
+				{
273
+					written = snprintf(cur, ALLOC_CHUNK,
274
+						"%s ", token->op->str);
275
+					written--;
276
+				}
277
+				break;
278
+			case RPN_arg:
279
+				written = snprintf(cur, ALLOC_CHUNK,
280
+					"A%lu ", token->arg_n);
281
+				break;
282
+			case RPN_val:
283
+				written = snprintf(cur, ALLOC_CHUNK,
284
+					"0x%lX ", token->value);
285
+				break;
286
+			default:
287
+				err = EUCLEAN;
288
+				goto free_err;
289
+		}
290
+		#ifdef DEBUG
291
+		if(written > ALLOC_CHUNK)
292
+		{
293
+			dprintf(2, "Expression too long : %s",
294
+				token->op->str);
295
+			err = EINVAL;
296
+			goto free_err;
297
+		}
298
+		#endif
299
+		cur += written;
300
+	}
301
+
302
+	return expr;
303
+
304
+	free_err:
305
+		free(expr);
306
+	ret_err:
307
+		errno = err;
308
+		return NULL;
309
+}
310
+
311
+const rpn_op_t* rpn_match_token(const char* token)
312
+{
313
+	int rep;
314
+	if((rep = rpn_match_token_i(token)) < 0)
315
+	{
316
+		return NULL;
317
+	}
318
+	return &(rpn_ops[rep]);
319
+}
320
+
321
+int rpn_match_token_i(const char* token)
322
+{
323
+	unsigned char i;
324
+	if(token[1] == '\0') //strlen(token) == 1
325
+	{
326
+		foreach_rpn_ops(i)
327
+		{
328
+			if(rpn_ops[i].chr == token[0])
329
+			{
330
+				return (int)i;
331
+			}
332
+		}
333
+		return -1;
334
+	}
335
+	foreach_rpn_ops(i)
336
+	{
337
+		if(!strncmp(rpn_ops[i].str, token, 8))
338
+		{
339
+			return (int)i;
340
+		}
341
+	}
342
+	return -1;
343
+}
344
+
345
+int rpn_match_number(const char* token, unsigned long *result)
346
+{
347
+	char *endptr;
348
+	unsigned long long res;
349
+	int base;
350
+
351
+	if(*token == '\0')
352
+	{
353
+		return -1;
354
+	}
355
+	base = 10;
356
+	if(*token == '0' && token[1] != '\0')
357
+	{
358
+		token++;
359
+		if(*token == 'x')
360
+		{
361
+			token++;
362
+			base = 16;
363
+		}
364
+		else if(*token == 'o')
365
+		{
366
+			token++;
367
+			base = 8;
368
+		}
369
+		else if(*token == 'b')
370
+		{
371
+			token++;
372
+			base = 2;
373
+		}
374
+	}
375
+	res = strtoull(token, &endptr, base);
376
+	if(*endptr != '\0')
377
+	{
378
+		return -1;
379
+	}
380
+	*result = res;
381
+	return 0;
382
+}
383
+
384
+size_t rpn_op_sz()
385
+{
386
+	return sizeof(rpn_ops)/sizeof(rpn_op_t);
387
+}

+ 326
- 0
rpn_parse.h View File

@@ -0,0 +1,326 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#ifndef __rpn_parse__h__
20
+#define __rpn_parse__h__
21
+
22
+#include <stdlib.h>
23
+#include <stdio.h>
24
+#include <string.h>
25
+#include <errno.h>
26
+
27
+#include "config.h"
28
+#include "rpn_lib.h"
29
+
30
+/**@file rpn_parse.h
31
+ * @brief RPN expression parsing headers
32
+ * @ingroup rpn_tokenize
33
+ *
34
+ * Contains headers of @ref rpn_tokenize and @ref rpn_parse .
35
+ */
36
+/**@defgroup rpn_tokenize Expression tokenization
37
+ * @brief Parsing an expression into a @ref rpn_tokenized_t
38
+ * 
39
+ * The tokenized form ( see @ref rpn_tokenized_t ) of an expression is usefull
40
+ * for @ref mutation.
41
+ *
42
+ * The tokenizing process is done in a way allowing compilation process to
43
+ * fetch tokens while parsing the expression (see @ref rpn_tok).
44
+ * @ingroup rpn_compile
45
+ */
46
+/**@defgroup rpn_parse Token parsing functions
47
+ * @brief Internal parsing functions
48
+ * @ingroup rpn_tokenize
49
+ */
50
+
51
+/**@brief Shortcut for loop on all operations list */
52
+#define foreach_rpn_ops(IDX) for(IDX=0; IDX<rpn_op_sz(); IDX++)
53
+
54
+/**@brief Check if a tokenizer is in error state
55
+ * @param tokenizer Pointer on a @ref rpn_tokenizer_s
56
+ * @return false if no error else true
57
+ * @note test if first chr of @ref rpn_tokenizer_s::err_reason is "\0"
58
+ */
59
+#define rpn_tokenizer_error(tokenizer) (*((tokenizer)->err_reason))
60
+
61
+/**@brief Shortcut for struct @ref rpn_op_s */
62
+typedef struct rpn_op_s rpn_op_t;
63
+/**@brief Shortcut for struct @ref rpn_token_type_e */
64
+typedef enum rpn_token_type_e rpn_token_type_t;
65
+/**@brief Shortcut for struct @ref rpn_token_s */
66
+typedef struct rpn_token_s rpn_token_t;
67
+/**@brief Shortcut for struct @ref rpn_tokenized_s */
68
+typedef struct rpn_tokenized_s rpn_tokenized_t;
69
+/**@brief Shortcut for struct @ref rpn_tokenizer_s */
70
+typedef struct rpn_tokenizer_s rpn_tokenizer_t;
71
+
72
+/**@brief Handles operation identification informations storage
73
+ * @ingroup rpn_tokenize
74
+ */
75
+struct rpn_op_s
76
+{
77
+	/**@brief Pointer on function pointer */
78
+	const void **fun;
79
+	/**@brief Function code size */
80
+	const unsigned long *fun_sz;
81
+	/**@brief Caracter representing operation ('\0' if None)*/
82
+	char chr;
83
+	/**@brief String representing operation */
84
+	char *str;
85
+};
86
+
87
+/**@brief Defines @ref rpn_token_s types
88
+ * @ingroup rpn_tokenize */
89
+enum rpn_token_type_e {
90
+	/**@brief The token is an operation */
91
+	RPN_op,
92
+	/**@brief The token is an argument */
93
+	RPN_arg,
94
+	/**@brief The token is a value */
95
+	RPN_val
96
+};
97
+
98
+/**@brief Represent an expression token (value, argument or operation)
99
+ * @ingroup rpn_tokenize */
100
+struct rpn_token_s
101
+{
102
+	/**@brief Token type */
103
+	rpn_token_type_t type;
104
+
105
+	/**@brief Token data depending on @ref type */
106
+	union {
107
+		/**@brief Token data for @ref RPN_op tokens */
108
+		struct {
109
+			/**@brief Indicate the operation index in @ref rpn_ops */
110
+			unsigned char op_n;
111
+			/**@brief Pointer on operation informations */
112
+			const rpn_op_t *op;
113
+		};
114
+		/**@brief Indicate the argument number */
115
+		unsigned long int arg_n;
116
+		/**@brief Indicate the constant value */
117
+		unsigned long int value;
118
+	};
119
+};
120
+//} __attribute__((aligned));
121
+
122
+/**@brief Represent a tokenized expression
123
+ *
124
+ * A list of @ref rpn_token_s and argc
125
+ * @ingroup rpn_tokenize */
126
+struct rpn_tokenized_s
127
+{
128
+	/**@brief Number of expected arguments */
129
+	size_t argc;
130
+	/**@brief The number of token in the expression */
131
+	size_t tokens_sz;
132
+	/**@brief List of tokens */
133
+	rpn_token_t *tokens;
134
+};
135
+
136
+/**@brief Handles data will tokenizing
137
+ *
138
+ * Store compilation state, allowing to return new token as soon as they
139
+ * become ready.
140
+ * @ingroup rpn_tokenize */
141
+struct rpn_tokenizer_s
142
+{
143
+	/**@brief Source expression */
144
+	const char *orig;
145
+	/**@brief Expression work buffer */
146
+	char *buff;
147
+	/**@brief Current expression buffer */
148
+	char *cur;
149
+	/**@brief Current chr number (for error generation & debugging) */
150
+	size_t chr_no;
151
+
152
+	/**@brief The tokenized representation of the expression
153
+	 * @note Should point on @ref rpn_expr_t::toks */
154
+	rpn_tokenized_t *toks;
155
+	/**@brief The number of allocated rpn_token_t in toks */
156
+	size_t allocated_toks;
157
+
158
+	/**@brief Tokenization error */
159
+	char err_reason[64];
160
+};
161
+
162
+/**@brief Define all operations
163
+ *
164
+ * Stores operation identification informations
165
+ * @ingroup rpn_tokenize */
166
+extern const rpn_op_t rpn_ops[];
167
+
168
+/**@brief Initialize a tokenizer and a tokenized representation
169
+ * @param tokenizer Pointer on a new tokenizer
170
+ * @param dst Pointer on a tokenized struct to store generated tokens
171
+ * @param expr Pointer on the RPN expression to tokenize
172
+ * @param argc Number of argument accepted by expression
173
+ * @return 0 if no error else -1 and set @ref rpn_tokenizer_s::err_reason
174
+ * @warning no NULL checks for the moment...
175
+ * @ingroup rpn_tokenize
176
+ */
177
+int rpn_tokenizer_start(rpn_tokenizer_t *tokenizer, rpn_tokenized_t *dst,
178
+	const char* expr, size_t argc);
179
+
180
+/**@brief Return the next token
181
+ * @param tokenizer Pointer on tokenizing task informations
182
+ * @return The a pointer on next @ref rpn_token_s in @ref rpn_tokenizer_s::toks
183
+ * or NULL if end of expression or error
184
+ * @note When NULL is returned all ressources are freed, no need to 
185
+ * call @ref rpn_tokenizer_free
186
+ * @ingroup rpn_tokenize
187
+ */
188
+rpn_token_t* rpn_tok(rpn_tokenizer_t *tokenizer);
189
+
190
+/**@brief Free ressources of a tokenizer
191
+ * @param tokenizer Pointer on the tokenizer we want to deallocate
192
+ * @note This method must be used to abord a tokenizing process with no
193
+ * error or end of expression encountered
194
+ * @ingroup rpn_tokenize
195
+ */
196
+void rpn_tokenizer_free(rpn_tokenizer_t *tokenizer);
197
+
198
+/**@brief Tokenize a '\0' terminated string
199
+ * @param token A '\0' terminated string
200
+ * @param dst Pointer on information destination
201
+ * @param error Pointer on an error reason buffer
202
+ * @return 0 if dst set and token recognized else -1 and set error buffer
203
+ * @warning assert token is not empty
204
+ * @ingroup rpn_tokenize
205
+ */
206
+int rpn_tokenize(const char *token, rpn_token_t *dst, char error[64]);
207
+
208
+/**@brief Represented a tokenized expression in a string
209
+ * @param tokens Tokenized expression
210
+ * @param long_op If true uses @ref rpn_op_s::str else @ref rpn_op_s::chr
211
+ * @return A newly allocated char* that should be deallocated using free()
212
+ * @ingroup rpn_tokenize
213
+ */
214
+char* rpn_tokenized_expr(rpn_tokenized_t *tokens, char long_op);
215
+
216
+/**@brief Returns NULL or a pointer on corresponding operation infos
217
+ * @param token The token we want to match
218
+ * @return NULL or operation informations
219
+ * @ingroup rpn_parse
220
+ */
221
+const rpn_op_t* rpn_match_token(const char* token);
222
+/**@brief Return -1 or an index corresponding to @ref rpn_ops
223
+ * @param token The token we want to match
224
+ * @return NULL or operation informations
225
+ * @ingroup rpn_parse
226
+ */
227
+int rpn_match_token_i(const char* token);
228
+
229
+/**@brief Get an integer from a token 
230
+ * @param token The token to decode
231
+ * @param result A pointer on the result
232
+ * @return -1 if given token is not a decimal number else 0 is returned
233
+ * and result is set
234
+ * @ingroup rpn_parse
235
+ */
236
+int rpn_match_number(const char* token, unsigned long *result);
237
+
238
+/**@brief Get operations list size
239
+ * @return number of operations in @ref rpn_ops
240
+ */
241
+size_t rpn_op_sz();
242
+
243
+/**@page rpn_lang RPN expression syntax
244
+ * @brief Howto write an expression
245
+ *
246
+ * \section rpn_lang_syntax General syntax
247
+ * An expression is composed of tokens separated by 1 or multiple separation
248
+ * characters (space, newline or tabs).
249
+ * 
250
+ * There is 3 types of token (see @ref rpn_token_type_e ) : @ref rpn_lang_op ,
251
+ * @ref rpn_lang_arg and @ref rpn_lang_value .
252
+ *
253
+ * \section rpn_lang_tokens RPN tokens
254
+ * \subsection rpn_lang_arg Arguments
255
+ * Expression can be parametric : arguments are given at evaluation and
256
+ * replaced in expression.
257
+ *
258
+ * In RPN epxressions arguments are desgined by a number (starting from 0)
259
+ * and preffixed by 'A' char.
260
+ *
261
+ * For example an expression evaluating to the sum of their two arguments
262
+ * will be written : "A0 A1 +"
263
+ *
264
+ * \subsection rpn_lang_value Constant values
265
+ * Constant values can be expressed in different bases (the Python syntax) :
266
+ * - 42
267
+ * - 0x2a or 0x2A
268
+ * - 0o52
269
+ * - 0b101010
270
+ *
271
+ * \subsection rpn_lang_op Operations
272
+ * Operations have two form : a short (1 character long) and a long (a string).
273
+ * 
274
+ * Each valid operations are declared in @ref rpn_ops variable (see 
275
+ * @ref rpn_parse.c for details).
276
+ *
277
+ * The @ref python_module expose a function pyrpn.get_ops() ( @see pyrpn_ops )
278
+ * returning a dict with long operations as key and short as value.
279
+ * \subsubsection rpn_lan_op_internal Internal mechanism
280
+ * Operations are done using a loopstack : operands are poped from stack, and
281
+ * the result is pushed onto it.
282
+ *
283
+ * Operations implementation are wrote in x86_64 linux assembly code ( see
284
+ * @ref rpn_lib.asm ). Each operations has a corresponding exported symbol
285
+ * (declared in @ref rpn_lib.h ) pointing compiled code. This code will be
286
+ * copied in a memory map in order to compile (@ref rpn_cmap ) an evaluation
287
+ * function.
288
+ */
289
+/**@page rpn_lang_ext Language extension
290
+ * @brief Howto add new operations
291
+ *
292
+ * @section rpn_lang_ext_op Add an operation
293
+ *
294
+ * In order to add a new operation you have to do three things :
295
+ *
296
+ * @subsection rpn_lang_ext_asm Write the operation code
297
+ *
298
+ * You have to write the operation code in @ref rpn_lib.asm and to expose
299
+ * the corresponding symbols (the code label and the code portion size).
300
+ *
301
+ * The macro part_sz allows to do most of the work by :
302
+ * - defining a symbol NAME_sz pointing on the code portion size
303
+ * - export the symbols NAME and NAME_sz
304
+ * 
305
+ * @warning The part_sz macro HAS TO be placed at the end of the corresponding
306
+ * code_portion
307
+ *
308
+ * @subsection rpn_lang_ext_head Import the symbols in C headers
309
+ * 
310
+ * The @ref rpn_lib.h header is designed to contain all extern assembly symbols
311
+ * (pointer on compiled code and on size).
312
+ *
313
+ * To add a new operation you have to "import" the symbols defined in
314
+ * @ref rpn_lib.asm using the @ref CODE_PART macro
315
+ *
316
+ * @subsection rpn_lang_ext_code Declare corresponding short and long tokens
317
+ *
318
+ * The @ref rpn_compile will match short or long operations and corresponding
319
+ * pre-compiled code.
320
+ *
321
+ * The association between short (char) long (char*) and pre-compiled code is
322
+ * done in the @ref rpn_ops variable.
323
+ * @note The __op macro allow simple operation declaration.
324
+ */
325
+
326
+#endif

+ 257
- 0
test.c View File

@@ -0,0 +1,257 @@
1
+/*
2
+ * Copyright (C) 2020 Weber Yann
3
+ * 
4
+ * This file is part of pyrpn.
5
+ * 
6
+ * pyrpn is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * pyrpn is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with pyrpn.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+#include "config.h"
20
+
21
+#include <stdlib.h>
22
+#include <unistd.h>
23
+#include <stdio.h>
24
+#include <errno.h>
25
+#include <string.h>
26
+#include <sys/mman.h>
27
+
28
+#include "rpn_lib.h"
29
+#include "rpn_jit.h"
30
+#include "rpn_parse.h"
31
+
32
+int test0()
33
+{
34
+	unsigned long res;
35
+
36
+	rpn_expr_t expr;
37
+	rpn_run_f expr_run;
38
+
39
+	expr.stack_sz = 8;
40
+	expr.args_count = 0;
41
+
42
+	if(_rpn_expr_init_map(&expr) < 0)
43
+	{
44
+		perror("Error starting map");
45
+		return 1;
46
+	}
47
+
48
+	unsigned long val;
49
+	/*
50
+	val = 2;
51
+	CODE_VALUE_CPY(&expr, val);
52
+	val = 40;
53
+	CODE_VALUE_CPY(&expr, val);
54
+	CODE_VALUE_CPY(&expr, val);
55
+	CODE_VALUE_CPY(&expr, val);
56
+	CODE_VALUE_CPY(&expr, val);
57
+	CODE_VALUE_CPY(&expr, val);
58
+	CODE_VALUE_CPY(&expr, val);
59
+	CODE_VALUE_CPY(&expr, val);
60
+	CODE_VALUE_CPY(&expr, val);
61
+	CODE_VALUE_CPY(&expr, val);
62
+	CODE_PART_CPY(&expr, rpn_add);
63
+	CODE_PART_CPY(&expr, rpn_add);
64
+	CODE_PART_CPY(&expr, rpn_add);
65
+	CODE_PART_CPY(&expr, rpn_add);
66
+	CODE_PART_CPY(&expr, rpn_add);
67
+	CODE_PART_CPY(&expr, rpn_add);
68
+	CODE_PART_CPY(&expr, rpn_add);
69
+	CODE_PART_CPY(&expr, rpn_add);
70
+	CODE_PART_CPY(&expr, rpn_add);
71
+	*/
72
+	val = 1;
73
+	CODE_ARG_CPY(&expr, val);
74
+	val = 0;
75
+	CODE_ARG_CPY(&expr, val);
76
+	CODE_PART_CPY(&expr, rpn_add);
77
+
78
+	if(_rpn_expr_end_map(&expr) < 0)
79
+	{
80
+		perror("Error ending map");
81
+		return 2;
82
+	}
83
+
84
+	expr_run = expr.code_map;
85
+
86
+	long unsigned int args[4] = {1337,42,0,4242};
87
+	//printf("Ready to run !\n");
88
+	res = expr_run(8, args, NULL);
89
+	//printf("Tada : %ld %p\n", res, res);
90
+	res++;
91
+
92
+	return 0;
93
+}
94
+
95
+int test_add()
96
+{
97
+	unsigned long res;
98
+
99
+	rpn_expr_t expr;
100
+	if(rpn_expr_init(&expr, 8, 0) < 0 ||
101
+		rpn_expr_compile(&expr, "16 26 +"))
102
+	{
103
+		dprintf(2, "Error initializing expr");
104
+		return 1;
105
+	}
106
+	res = rpn_expr_eval(&expr, NULL);
107
+	//printf("Result = %ld\n", res);
108
+	if(res != 42)
109
+	{
110
+		dprintf(2, "Error : expected 42 but %ld received\n",
111
+			res);
112
+		return 2;
113
+	}
114
+	rpn_expr_close(&expr);
115
+	return 0;
116
+}
117
+
118
+int test_args()
119
+{
120
+	unsigned long res, args[2];
121
+
122
+	rpn_expr_t expr;
123
+
124
+	if(rpn_expr_init(&expr, 8, 2))
125
+	{
126
+		dprintf(2, "Error initializing expr");
127
+		return 1;
128
+	}
129
+	if(rpn_expr_compile(&expr, "A0 A1 +") < 0)
130
+	{
131
+		dprintf(2, "Compilation error : %s\n",
132
+			expr.err_reason);
133
+		return 1;
134
+	}
135
+
136
+	args[0] = 16;
137
+	args[1] = 26;
138
+	res = rpn_expr_eval(&expr, args);
139
+	//printf("Result = %ld\n", res);
140
+	if(res != 42)
141
+	{
142
+		dprintf(2, "Error : expected 42 but %ld received\n",
143
+			res);
144
+		return 2;
145
+	}
146
+	rpn_expr_close(&expr);
147
+	return 0;
148
+}
149
+
150
+int test_stack_sz()
151
+{
152
+	rpn_expr_t expr;
153
+	int stack_sz, i;
154
+	unsigned long res, args[2];
155
+
156
+	for(stack_sz=4; stack_sz<256; stack_sz++)
157
+	{
158
+		if(rpn_expr_init(&expr, stack_sz, 2))
159
+		{
160
+			dprintf(2, "Error initializing expr");
161
+			return 1;
162
+		}
163
+		if(rpn_expr_compile(&expr,
164
+"+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ") < 0)
165
+		{
166
+			dprintf(2, "Compilation error : %s\n",
167
+				expr.err_reason);
168
+			return 1;
169
+		}
170
+
171
+		args[0] = 16;
172
+		args[1] = 26;
173
+		for(i=0; i<256; i++)
174
+		{
175
+			res = rpn_expr_eval(&expr, args);
176
+//dprintf(2, "DEBUG : eval %d:%d res = %ld\n", stack_sz, i, res);
177
+			//printf("Result = %ld\n", res);
178
+			if(res != 0)
179
+			{
180
+				dprintf(2, "Error : expected 0 but %ld received\n",
181
+					res);
182
+				return 2;
183
+			}
184
+		}
185
+		rpn_expr_close(&expr);
186
+	}
187
+	return 0;
188
+}
189
+
190
+
191
+int test_tokenization()
192
+{
193
+	rpn_expr_t expr;
194
+	int stack_sz, argc, i;
195
+	char *expr_orig, *expr_untok;
196
+
197
+
198
+	stack_sz = 32;
199
+	argc=4;
200
+	for(i=0;i<20;i++)
201
+	{
202
+		if(rpn_expr_init(&expr, stack_sz, argc))
203
+		{
204
+			dprintf(2, "Error initializing expr");
205
+			return 1;
206
+		}
207
+		expr_orig = rpn_random(20+i, argc);
208
+		if(rpn_expr_compile(&expr, expr_orig) < 0)
209
+		{
210
+			dprintf(2, "Compilation error : %s\n",
211
+				expr.err_reason);
212
+			return 1;
213
+		}
214
+		expr_untok = rpn_tokenized_expr(&(expr.toks), 0);
215
+		if(strcmp(expr_untok, expr_orig))
216
+		{
217
+			dprintf(2,
218
+"Untokenized str differ from original : \"%s\" != \"%s\"\n",
219
+				expr_orig, expr_untok);
220
+			return 1;
221
+		}
222
+		rpn_expr_close(&expr);
223
+		free(expr_untok);
224
+	}
225
+
226
+	return 0;
227
+}
228
+
229
+
230
+
231
+
232
+#define RUNTEST(NAME) 		\
233
+printf("Running %s\n", #NAME);	\
234
+printf("%s()\t", #NAME);	\
235
+fsync(1);			\
236
+if(NAME())			\
237
+{				\
238
+	printf("[failed]\n");	\
239
+	res++;			\
240
+}				\
241
+else				\
242
+{				\
243
+	printf("[OK]\n");	\
244
+}
245
+
246
+int main()
247
+{
248
+	int res;
249
+
250
+	res = 0;
251
+	//RUNTEST(test0);
252
+	RUNTEST(test_add);
253
+	RUNTEST(test_args);
254
+	RUNTEST(test_stack_sz);
255
+	RUNTEST(test_tokenization);
256
+	return res;
257
+}

+ 0
- 0
tests/__init__.py View File


+ 51
- 0
tests/benchmark.py View File

@@ -0,0 +1,51 @@
1
+#!/usr/bin/python3
2
+
3
+import sys
4
+import random
5
+import time
6
+
7
+try:
8
+    import pyrpn
9
+except (ImportError, NameError) as e:
10
+    print("Error importing pyrpn. Try to run make.",
11
+          file=sys.stderr)
12
+    raise e
13
+
14
+
15
+expr_count = 0x200
16
+max_iter = 0x3000
17
+argc = 2
18
+sz = 0x10
19
+
20
+if len(sys.argv) > 1:
21
+    expr_count = int(sys.argv[1], 0)
22
+if len(sys.argv) > 2:
23
+    max_iter = int(sys.argv[2], 0)
24
+if len(sys.argv) > 3:
25
+    argc = int(sys.argv[3], 0)
26
+if len(sys.argv) > 4:
27
+    sz = int(sys.argv[4], 0)
28
+
29
+tot = 0
30
+time_op = 0
31
+start = time.time()
32
+IMAX = (1<<63)-1
33
+samples = 8
34
+rnd_samples = [[[random.randint(0, IMAX) for _ in range(argc)] for _ in range(max_iter)]
35
+               for _ in range(samples)]
36
+for i in range(expr_count):
37
+    rnd_expr = pyrpn.random_expr(argc, sz)
38
+    all_start = time.time()
39
+    expr = pyrpn.RPNExpr(rnd_expr, argc)
40
+    start_op = time.time()
41
+    rnds = random.choice(rnd_samples)
42
+    for rnd in rnds:
43
+        expr.eval(*rnd)
44
+    time_op += time.time() - start_op
45
+    tot += time.time() - all_start
46
+    sys.stderr.write(".")
47
+    sys.stderr.flush()
48
+sys.stderr.write("\n")
49
+total = time.time() - start
50
+print("Runned %.2fs" % total)
51
+print("%.1fK iter/s %.1fms per expr" % (max_iter*expr_count/time_op/1000, tot/expr_count*1000))

+ 1
- 0
tests/pyrpn.so View File

@@ -0,0 +1 @@
1
+../pyrpn.so

+ 275
- 0
tests/tests_rpn.py View File

@@ -0,0 +1,275 @@
1
+#!/usr/bin/python3
2
+# Copyright 2020 Weber Yann
3
+#
4
+# This file is part of geneifs.
5
+#
6
+#        geneifs is free software: you can redistribute it and/or modify
7
+#        it under the terms of the GNU General Public License as published by
8
+#        the Free Software Foundation, either version 3 of the License, or
9
+#        (at your option) any later version.
10
+#
11
+#        geneifs is distributed in the hope that it will be useful,
12
+#        but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+#        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+#        GNU General Public License for more details.
15
+#
16
+#        You should have received a copy of the GNU General Public License
17
+#        along with geneifs.  If not, see <http://www.gnu.org/licenses/>.
18
+#
19
+
20
+import sys
21
+import random
22
+import math
23
+
24
+import unittest
25
+from unittest import skip
26
+
27
+IMAX = (1<<64)-1
28
+
29
+try:
30
+    import pyrpn
31
+except (ImportError, NameError) as e:
32
+    print("Error importing pyrpn. Try to run make.",
33
+          file=sys.stderr)
34
+    raise e
35
+
36
+class Test0RpnModule(unittest.TestCase):
37
+
38
+    def test_init(self):
39
+        """ RPNExpr instanciation """
40
+        expr = pyrpn.RPNExpr("42", 2)
41
+
42
+    def test_init_badargs(self):
43
+        """ RPNExpr instanciation with bad arguments """
44
+        badargs = [('', 2), (), ('ab+',), ('ab+',300), (42, 42), ('ab', '42')]
45
+        for badarg in badargs:
46
+           with self.assertRaises((ValueError, TypeError)):
47
+            expr = pyrpn.RPNExpr(*badarg)
48
+
49
+    def test_init_loop(self):
50
+        """ Testing pyrpn.RPNExpr multiple instanciation """
51
+        exprs = []
52
+        for i in range(256):
53
+            expr = pyrpn.RPNExpr("42", 2)
54
+
55
+    def test_init_args(self):
56
+        """ Testing pyrpn.RPNExpr instanciation with arguments """
57
+        for argc in range(0, 256):
58
+            with self.subTest("RPNExpr('42', %d)" % argc):
59
+                expr = pyrpn.RPNExpr("42", argc)
60
+
61
+    def test_init_stack_sz(self):
62
+        """ Instanciate RPNExpr specifiing a stack_size """
63
+        for argc in range(0,256, 8):
64
+            for sz in range(4, 256, 2):
65
+                with self.subTest("RPNExpr('42', %d,%d)" % (argc, sz)):
66
+                    expr = pyrpn.RPNExpr("42", argc, sz)
67
+
68
+    def test_op_dict(self):
69
+        """ Testing RPNExpr.get_ops() method """
70
+        known_ops = dict([
71
+            ('add', '+'),
72
+            ('sub', '-'),
73
+            ('mul', '*'),
74
+            ('div', '/'),
75
+            ('mod', '%'),
76
+            ('neg', '!'),
77
+            ('not', '~'),
78
+            ('and', '&'),
79
+            ('or', '|'),
80
+            ('xor', '^'),
81
+            ('>>', 'r'),
82
+            ('<<', 'l'),
83
+            ('xchg', 'x'),
84
+            ('dup', 'd'),
85
+            ('pop', 'p'),
86
+        ])
87
+        for long, short in pyrpn.get_ops().items():
88
+            self.assertIn(long, known_ops)
89
+            self.assertEqual(short, known_ops[long])
90
+
91
+    def test_rand_expr(self):
92
+        """ Testing RPNExpr.random_expr() """
93
+        result = {}
94
+        counters = {'vals': 0, 'args': 0}
95
+        all_count = 0
96
+        for sz in range(1,64,3):
97
+            for argc in range(1,128):
98
+                rnd_expr = pyrpn.random_expr(argc, sz)
99
+                #print(repr(rnd_expr))
100
+                for tok in rnd_expr.split(' '):
101
+                    all_count += 1
102
+                    if len(tok) == 0:
103
+                        continue
104
+                    try:
105
+                        itok = int(tok, 0)
106
+                        counters['vals'] += 1
107
+                        continue
108
+                    except Exception:
109
+                        pass
110
+                    if tok[0] == 'A':
111
+                        counters['args'] += 1
112
+                    else:
113
+                        if tok not in counters:
114
+                            counters[tok] = 0
115
+                        counters[tok] += 1
116
+        all_ops = len(pyrpn.get_ops()) + 2
117
+        entropy = 1-sum([(n/all_count)**2
118
+                          for _, n in counters.items()])
119
+        self.assertGreater(entropy, 1-(1/all_ops), "Low entropy !")
120
+                
121
+
122
+class TestRpnCompile(unittest.TestCase):
123
+
124
+    def test_basic(self):
125
+        """ Compile 6 7 * """
126
+        expr = pyrpn.RPNExpr("6 7 *", 2)
127
+
128
+    def test_base_error(self):
129
+        """ Compile error a b + """
130
+        with self.assertRaises(ValueError):
131
+            expr = pyrpn.RPNExpr("a b +", 3)
132
+
133
+    def test_various_compile(self):
134
+        """ Compile various expressions """
135
+        exprs = (("42 2 + * /", 0),
136
+            ("A1 A2 A0 * + /", 3),
137
+        )
138
+        for expr, argc in exprs:
139
+            res = pyrpn.RPNExpr(expr, argc)
140
+
141
+    def test_long_code(self):
142
+        """ Compile long expressions """
143
+        for i in range(64,256,2):
144
+            for j in range(256,4):
145
+                exprs = ' '.join(['+' for _ in range(i)])
146
+                expr = pyrpn.RPNExpr(exprs, j)
147
+            
148
+
149
+class TestRpnEval(unittest.TestCase):
150
+
151
+    def test_arithm(self):
152
+        """ Tests arithmetic ops """
153
+        ops = [
154
+            ('+', '+'),
155
+            ('-', '-'),
156
+            ('*', '*'),
157
+            ('/', '//'),
158
+            ('%', '%'),
159
+            ('&', '&'),
160
+            ('|', '|'),
161
+            ('^', '^'),
162
+            #('l', '<<'),
163
+            #('r', '>>')
164
+        ]
165
+        for rpn_op, pyop in ops:
166
+            with self.subTest('Testing op %s (%s)' % (rpn_op, pyop)):
167
+                sys.stderr.write('.')
168
+                sys.stderr.flush()
169
+                for i in range(0x1000):
170
+                    op1, op2 = random.randint(0,IMAX), random.randint(1,IMAX)
171
+                    pyexpr = '%d %s %d' % (op1, pyop, op2)
172
+                    pyval = eval(pyexpr) % (1<<64)
173
+                    rpn_expr = "%d %d %s" % (op1, op2, rpn_op)
174
+                    expr = pyrpn.RPNExpr(rpn_expr, 0)
175
+                    res = expr.eval()
176
+                    self.assertEqual(res, pyval,
177
+                        "TEST#%d %s != %s" % (i, rpn_expr, pyexpr))
178
+
179
+    def test_not(self):
180
+        """ Test unary op """
181
+        ops = [('~', '~%d'), ('!', '-%d')]
182
+        for rpn_op, pystr in ops:
183
+            with self.subTest("Testing op '%sX' (%s)" % (rpn_op, pystr)):
184
+                sys.stderr.write('.')
185
+                sys.stderr.flush()
186
+                for i in range(0x1000):
187
+                    op1 = random.randint(0, IMAX)
188
+                    pyexpr = pystr % op1
189
+                    pyval = eval(pyexpr) % (1<<64)
190
+                    rpn_expr = '%d %s' % (op1, rpn_op)
191
+                    expr = pyrpn.RPNExpr(rpn_expr, 0)
192
+                    res = expr.eval()
193
+                    self.assertEqual(res, pyval,
194
+                        "TEST#%d %s != %s" % (i, rpn_expr, pyexpr))
195
+
196
+    def test_div_zero(self):
197
+        """ Test division by zeros """
198
+        tests = ['A0 0 /', 'A0 0 %']
199
+        for test in tests:
200
+            with self.subTest('Testing division by zero using %s' % test):
201
+                for i in range(10):
202
+                    expr = pyrpn.RPNExpr(test, 1)
203
+                    res = expr.eval(random.randint(0, IMAX))
204
+                    self.assertEqual(res, 0)
205
+
206
+    def test_stack_init(self):
207
+        """ testing that stack is initialized to 0 """
208
+        rpn = ' '.join(['+' for _ in range(15)])
209
+        for stack_size in range(4, 128):
210
+            with self.subTest('Stack with size %d initialized to 0' % stack_size):
211
+                for argc in range(0,256,16):
212
+                    expr = pyrpn.RPNExpr(rpn, argc, stack_size)
213
+                    r = expr.eval(*[random.randint(0, IMAX)
214
+                                    for _ in range(argc)])
215
+                    self.assertEqual(r, 0,
216
+                        '"+ + + +..." should be 0 but %d returned with %d args' % (r, argc))
217
+
218
+
219
+    def test_lshift_limit(self):
220
+        """ 2 << 0x10000 == 0 ? """
221
+        expr = pyrpn.RPNExpr("2 %d <<" % 0x100000, 0)
222
+        res = expr.eval()
223
+        self.assertEqual(res, 0)
224
+
225
+    def test_rshift_limit(self):
226
+        """ (1<<64)-1 >> 0x10000 == 0 ? """
227
+        expr = pyrpn.RPNExpr("%d %d >>" % ((1<<64)-1, 0x100000), 0)
228
+        res = expr.eval()
229
+        self.assertEqual(res, 0)
230
+
231
+
232
+    def test_airthm_extended(self):
233
+        """ Extended arithmetic tests """
234
+        exprs = (
235
+            ('A0 A1 +', '{0} + {1}', 2),
236
+            ('A0 A0 A1 + +', '{0} + {1} + {0}', 2),
237
+            ('A1 A0 A0 A0 A0 A0 A0 A0 A0 + + + + + + +', '{0} * 8', 2),
238
+            ('+', '0', 2),
239
+            ('-', '0', 2),
240
+            ('A0 A1 -', '{0} - {1}', 2),
241
+            ('A0 A0 A1 - -', '{0} - ({0} - {1})', 2),
242
+            ('A0 0 A0 - -', '({0} - (0 - {0})) ', 2),
243
+            ('*', '0', 2),
244
+            ('A0 A1 *', '{0} * {1}', 2),
245
+            ('A0 A0 A1 * *', '{0} * {0} * {1}', 2),
246
+            ('A0 A0 A0 * *', '{0} * {0} * {0}', 2),
247
+            ('A0 A1 x /', '{1} // {0}', 2),
248
+            ('A0 A1 A0 pop /', '{0} // {1}', 2),
249
+            ('A0 A1 dup +', '{1} + {1}', 2),
250
+        )
251
+        for rpn, pye, argc in exprs:
252
+            expr = pyrpn.RPNExpr(rpn, argc, 8)
253
+            for _ in range(0x300):
254
+                args = tuple([random.randint(0,255) for _ in range(argc)])
255
+                with self.subTest('%s == %s %r' % (rpn, pye, args)):
256
+                    pyexpr = pye.format(*args)
257
+                    try:
258
+                        respy = eval(pyexpr) % (1<<64)
259
+                    except ZeroDivisionError:
260
+                        respy = 0
261
+                    res = expr.eval(*args)
262
+                    self.assertEqual(res, respy,
263
+                        '%s%r != %s' % (rpn, args, pyexpr))
264
+
265
+
266
+class SequentialTestLoader(unittest.TestLoader):
267
+    def getTestCaseNames(self, testCaseClass):
268
+        test_names = super().getTestCaseNames(testCaseClass)
269
+        testcase_methods = list(testCaseClass.__dict__.keys())
270
+        test_names.sort(key=testcase_methods.index)
271
+        return test_names
272
+
273
+
274
+if __name__ == '__main__':
275
+    unittest.main(testLoader=SequentialTestLoader())

Loading…
Cancel
Save