├── tinypy ├── __init__.py ├── tpy_dict.c ├── compiler │ ├── __init__.py │ ├── py2bc.py │ ├── asm.py │ ├── opcodes.py │ ├── boot.py │ ├── disasm.py │ ├── __main__.py │ └── tokenize.py ├── dummy-compiler.c ├── printf │ ├── test1.out │ ├── test1.gold │ ├── Makefile │ ├── test1.c │ ├── mini-printf.h │ ├── README.md │ └── mini-printf.c ├── tpmain.c ├── vmmain.c ├── tp_echo.c ├── compiler.c ├── tp.c ├── runtime │ ├── types.py │ └── testing.py ├── tp_ops.h ├── tp_internal.h ├── tp_import.c ├── repl.c ├── tp_data.c ├── tp_hash.c ├── tpy_list.c ├── tp_param.c ├── tp_number.h ├── tp_number.c ├── tp_func.c ├── tp_list.c ├── tpd_list.c ├── tp_frame.c ├── tp_meta.c ├── interp │ └── sandbox.c ├── tpd_dict.c ├── runtime.c ├── tp_interp.c ├── tp_dict.c ├── tpy_string.c ├── tp_repr.c └── tp_string.c ├── .gitignore ├── tpc ├── modules ├── dl │ ├── libffi-3.2.1.tar.gz │ ├── tests.py │ └── repl.py ├── random │ ├── init.c │ └── tests.py ├── math │ ├── init.c │ └── tests.py ├── re │ └── regexpr.h ├── pygame │ └── init.c └── jni │ └── init.c ├── java ├── test.py ├── Makefile ├── TinyPy.java └── libtinypy.c ├── tests ├── test_globals.py ├── test_testing.py ├── test_sys.py ├── test_dict.py ├── test_methods.py ├── test_object.py ├── test_list.py ├── test_attr_magics.py ├── test_number.py ├── test_str.py └── test_func.py ├── HACKING.txt ├── examples ├── julia.py ├── asteroid.py └── vines.py ├── README.rst ├── ROADMAP.txt ├── run-tests.sh ├── .github └── workflows │ └── main.yaml ├── CHANGES.txt ├── README.txt.old ├── LICENSE.txt ├── Makefile ├── cpython └── cpython.c └── doc └── A Brief description of re module of tinypy.tex /tinypy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tinypy/tpy_dict.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tinypy/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.tpc 3 | *.o 4 | -------------------------------------------------------------------------------- /tinypy/dummy-compiler.c: -------------------------------------------------------------------------------- 1 | #include "tp.h" 2 | 3 | void tp_module_compiler_init(TP) { } 4 | -------------------------------------------------------------------------------- /tpc: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from tinypy.compiler.__main__ import main 4 | 5 | main() 6 | -------------------------------------------------------------------------------- /modules/dl/libffi-3.2.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rainwoodman/tinypy/HEAD/modules/dl/libffi-3.2.1.tar.gz -------------------------------------------------------------------------------- /tinypy/printf/test1.out: -------------------------------------------------------------------------------- 1 | 0000001 2 0000000003 2 | 26 3 | FFFF*** FFFF*** 000000FFFFFFF 4 | 29 5 | OA007B010C R*** 6 | 15 7 | testing 1 2 0000003 8 | 19 9 | faster and "cheaper" 10 | 20 11 | deadf00d % DEADF00D 12 | 19 13 | 00000000100000000200000000300 14 | 29 15 | 50 50 -50 4294967246 16 | 20 17 | -------------------------------------------------------------------------------- /tinypy/printf/test1.gold: -------------------------------------------------------------------------------- 1 | 0000001 2 0000000003 2 | 26 3 | FFFF*** FFFF*** 000000FFFFFFF 4 | 29 5 | OA007B010C R*** 6 | 15 7 | testing 1 2 0000003 8 | 19 9 | faster and "cheaper" 10 | 20 11 | deadf00d % DEADF00D 12 | 19 13 | 00000000100000000200000000300 14 | 29 15 | 50 50 -50 4294967246 16 | 20 17 | -------------------------------------------------------------------------------- /java/test.py: -------------------------------------------------------------------------------- 1 | import jni 2 | 3 | TinyPy = jni.find_class("TinyPy") 4 | constructor = jni.get_method_id(TinyPy, "", "()V") 5 | tp = jni.new_object(TinyPy, constructor) 6 | say = jni.get_method_id(TinyPy, "say2", "(Ljava/lang/String;)V") 7 | jni.call_object_method(tp, say, "hello world") 8 | 9 | foo = "hi there" 10 | -------------------------------------------------------------------------------- /tinypy/printf/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = --std=c99 -O2 -pedantic -Wall -Wextra -Wshadow -DMINI_PRINTF_ENABLE_OBJECTS 2 | 3 | %_check: %.out %.gold 4 | cmp $^ 5 | @echo PASS 6 | 7 | all test: test1_check 8 | 9 | test1: mini-printf.o 10 | 11 | test1.out: test1 12 | ./test1 > $@ 13 | 14 | clean: 15 | rm -f *.o test1 test1.out 16 | -------------------------------------------------------------------------------- /java/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-fPIC -I$(JAVA_HOME)/../include/ -I$(JAVA_HOME)/../include/linux -pipe -g -Wall 2 | 3 | run: TinyPy.class libtinypy.so 4 | java -Djava.library.path=. -cp . TinyPy 5 | 6 | %.class: %.java 7 | javac $< 8 | 9 | libtinypy.so: libtinypy.c 10 | $(CC) -o $@ -shared -Wl,-soname,$@ $^ $(CFLAGS) 11 | 12 | clean: 13 | rm -f *.log TinyPy.class libtinypy.so 14 | -------------------------------------------------------------------------------- /tests/test_globals.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | foo = 3 4 | class MyTest(UnitTest): 5 | def setup(self, testname): 6 | print('setup', testname) 7 | 8 | def teardown(self, testname): 9 | print('teardown', testname) 10 | 11 | def test_globals(self): 12 | assert 'foo' in globals() 13 | assert 'bar' not in globals() 14 | 15 | t = MyTest() 16 | 17 | t.run() 18 | -------------------------------------------------------------------------------- /tests/test_testing.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class MyTest(UnitTest): 4 | def setup(self, testname): 5 | print('setup', testname) 6 | 7 | def teardown(self, testname): 8 | print('teardown', testname) 9 | 10 | def test_0_ok(self): 11 | return 12 | 13 | def known_failure_test_1_fail(self): 14 | assert 1 == 2 15 | 16 | t = MyTest() 17 | 18 | t.run() 19 | 20 | -------------------------------------------------------------------------------- /tinypy/compiler/py2bc.py: -------------------------------------------------------------------------------- 1 | from tinypy.compiler.boot import * 2 | import tinypy.compiler.tokenize as tokenize 3 | import tinypy.compiler.parse as parse 4 | import tinypy.compiler.encode as encode 5 | 6 | def compile(s, fname): 7 | tokens = tokenize.tokenize(s) 8 | t = parse.parse(s,tokens) 9 | r = encode.encode(fname,s,t) 10 | return r 11 | 12 | def main(src, dest): 13 | s = read(src) 14 | r = compile(s,src) 15 | save(dest, r) 16 | -------------------------------------------------------------------------------- /modules/dl/tests.py: -------------------------------------------------------------------------------- 1 | import dl 2 | 3 | sqrt = dl.load('libm.so.6', 'sqrt', 'd', 'd') 4 | 5 | for value in [2, 10, 100]: 6 | result = sqrt(value * value) 7 | print('sqrt(' + str(value * value) + ') = ' + str(result)) 8 | if result != value: 9 | raise "unexpected result" 10 | 11 | print(dl.size('III')) 12 | 13 | data = dl.pack('III', [1, 2, 3]) 14 | print(data) 15 | result = dl.unpack('III', data) 16 | for i in result: 17 | print(i) 18 | -------------------------------------------------------------------------------- /tinypy/tpmain.c: -------------------------------------------------------------------------------- 1 | #include "tp.h" 2 | 3 | /* INCLUDE */ 4 | 5 | void * _tp_import_modules(TP); 6 | 7 | /* from runtime */ 8 | tp_obj tp_load(TP, const char*); 9 | 10 | int main(int argc, char *argv[]) { 11 | tp_vm *tp = tp_init(argc, argv, 1); 12 | /* INIT */ 13 | _tp_import_modules(tp); 14 | 15 | tp_obj fname = tp_string_atom(tp, argv[1]); 16 | tp_obj source = tp_load(tp, argv[1]); 17 | tp_obj code = tp_compile(tp, source, fname); 18 | tp_obj module = tp_import(tp, tp_string_atom(tp, "__main__"), code, fname); 19 | 20 | tp_deinit(tp); 21 | return(0); 22 | } 23 | 24 | /**/ 25 | -------------------------------------------------------------------------------- /tinypy/vmmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tp.h" 3 | 4 | void * _tp_import_modules(TP); 5 | /* from runtime */ 6 | tp_obj tp_load(TP, const char*); 7 | 8 | int main(int argc, char *argv[]) { 9 | /* fixme: make this argc */ 10 | char * p = getenv("TP_DISABLE_PY_RUNTIME"); 11 | int enable_py_runtime = (p == NULL) || (*p == '0'); 12 | 13 | tp_vm *tp = tp_init(argc, argv, enable_py_runtime); 14 | 15 | tp_obj fname = tp_string_atom(tp, argv[1]); 16 | tp_obj code = tp_load(tp, argv[1]); 17 | 18 | tp_obj module = tp_import(tp, tp_string_atom(tp, "__main__"), code, fname); 19 | 20 | tp_deinit(tp); 21 | return(0); 22 | } 23 | -------------------------------------------------------------------------------- /HACKING.txt: -------------------------------------------------------------------------------- 1 | function namespaces: 2 | 3 | Functions are into several namespaces from lower-level to higher-level: 4 | 5 | - `tpd_*` : tinypy data structures; 6 | 7 | - `tp_*` : tinypy interpreter C-API; 8 | arguments from C arguments; 9 | functions may return any C value; 10 | return values are usually tracked by gc, 11 | unless the name indicates untracked (ending with _nt) 12 | add the result to gc before dropping to the Python land. 13 | 14 | - `tpy_*` : python language C-API 15 | arguments from local scope parameter list 16 | functions always return tp_obj; 17 | return values are usually tracked by gc if it should, 18 | unless the name indicates untracked. 19 | -------------------------------------------------------------------------------- /tests/test_sys.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from tinypy.runtime.testing import UnitTest 3 | 4 | class MyTest(UnitTest): 5 | def test_no_exc(self): 6 | assert sys.get_exc()[0] is None 7 | 8 | def test_str_exc(self): 9 | try: 10 | raise "Hello" 11 | except: 12 | exc, stack = sys.get_exc() 13 | assert exc == "Hello" 14 | assert "test_str_exc" in stack 15 | 16 | def test_sys_argv(self): 17 | assert len(sys.argv) > 0 18 | 19 | def test_sys_conf(self): 20 | assert sys.conf.gcmax >= 0 21 | old = sys.conf.gcmax 22 | sys.conf.gcmax = 16383 23 | assert sys.conf.gcmax == 16383 24 | sys.conf.gcmax = old 25 | 26 | t = MyTest() 27 | 28 | t.run() 29 | -------------------------------------------------------------------------------- /tinypy/tp_echo.c: -------------------------------------------------------------------------------- 1 | /* File: Miscellaneous 2 | * Various functions to help interface tinypy. 3 | */ 4 | 5 | void tp_echo(TP, tp_obj e) { 6 | e = tp_str(tp, e); 7 | tp->echo(tp_string_getptr(e), tp_string_len(e)); 8 | } 9 | 10 | char * tp_cstr(TP, tp_obj v) { 11 | char * buffer; 12 | char const * val; 13 | if(v.type.typeid != TP_STRING) { 14 | val = "NOT A STRING"; 15 | buffer = tp_malloc(tp, strlen(val) + 1); 16 | memcpy(buffer, val, strlen(val) + 1); 17 | } else { 18 | val = tp_string_getptr(v); 19 | buffer = tp_malloc(tp, tp_string_len(v) + 1); 20 | memset(buffer, 0, tp_string_len(v) + 1); 21 | memcpy(buffer, val, tp_string_len(v)); 22 | } 23 | 24 | return buffer; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /tests/test_dict.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from tinypy.runtime.testing import UnitTest 3 | 4 | class C: 5 | pass 6 | 7 | class MyTest(UnitTest): 8 | 9 | def test_eq(self): 10 | assert {'a':1, 'b':2} != {'a':1, 'c':2} 11 | assert {'a':1} == {'a':1} 12 | assert {'a':1, 'b':2} == {'a':1, 'b':2} 13 | 14 | def test_lessthan(self): 15 | try: 16 | assert {'a':1, 'b':2} < {'a':1, 'c':2} 17 | except: 18 | assert 'TypeError' in sys.get_exc()[0] 19 | 20 | def test_keyerror(self): 21 | try: 22 | {}['a'] 23 | except: 24 | assert "(tpd_dict_get) KeyError: a" in sys.get_exc()[0] 25 | 26 | def test_update(self): 27 | a = {} 28 | b = {'a' : 1} 29 | a.update(b) 30 | 31 | assert a['a'] == 1 32 | 33 | t = MyTest() 34 | 35 | t.run() 36 | -------------------------------------------------------------------------------- /tinypy/compiler.c: -------------------------------------------------------------------------------- 1 | #include "tp.h" 2 | 3 | #include "compiler/boot.c" 4 | #include "compiler/encode.c" 5 | #include "compiler/opcodes.c" 6 | #include "compiler/parse.c" 7 | #include "compiler/tokenize.c" 8 | #include "compiler/py2bc.c" 9 | 10 | void tp_module_compiler_init(TP) { 11 | tp_import_from_buffer(tp, 0, "tinypy.compiler.boot", _tp_boot_tpc, sizeof(_tp_boot_tpc)); 12 | tp_import_from_buffer(tp, 0, "tinypy.compiler.opcodes", _tp_opcodes_tpc, sizeof(_tp_opcodes_tpc)); 13 | tp_import_from_buffer(tp, 0, "tinypy.compiler.tokenize", _tp_tokenize_tpc, sizeof(_tp_tokenize_tpc)); 14 | tp_import_from_buffer(tp, 0, "tinypy.compiler.parse", _tp_parse_tpc, sizeof(_tp_parse_tpc)); 15 | tp_import_from_buffer(tp, 0, "tinypy.compiler.encode", _tp_encode_tpc, sizeof(_tp_encode_tpc)); 16 | tp_import_from_buffer(tp, 0, "tinypy.compiler.py2bc", _tp_py2bc_tpc, sizeof(_tp_py2bc_tpc)); 17 | } 18 | -------------------------------------------------------------------------------- /tests/test_methods.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class MyClass: 4 | a = 3 5 | a = a 6 | 7 | def __call__(self, a): 8 | return a 9 | 10 | def b(self, k): 11 | return k 12 | 13 | def f(k): 14 | return k 15 | s = staticmethod(f) 16 | 17 | class MyTest(UnitTest): 18 | 19 | def test_method(self): 20 | obj = MyClass() 21 | assert obj.b(3) == 3 22 | assert MyClass.b(obj, 3) == 3 23 | 24 | def test_static_attr(self): 25 | obj = MyClass() 26 | assert obj.a == 3 27 | assert MyClass.a == 3 28 | 29 | def test_staticmethod(self): 30 | obj = MyClass() 31 | assert MyClass.s(3) == 3 32 | assert MyClass.f(3) == 3 33 | assert obj.s(3) == 3 34 | 35 | def test_call(self): 36 | obj = MyClass() 37 | assert obj(3) == 3 38 | 39 | t = MyTest() 40 | 41 | t.run() 42 | -------------------------------------------------------------------------------- /tinypy/tp.c: -------------------------------------------------------------------------------- 1 | #include "tp.h" 2 | #include "tp_internal.h" 3 | 4 | tp_obj tp_None = {TP_NONE}; 5 | tp_obj tp_True = {.type = {TP_NUMBER, TP_NUMBER_INT}, .nint = 1}; 6 | tp_obj tp_False = {.type = {TP_NUMBER, TP_NUMBER_INT}, .nint = 0}; 7 | 8 | #include "tpd_list.c" 9 | #include "tpd_dict.c" 10 | 11 | #include "tp_echo.c" 12 | #include "tp_param.c" 13 | 14 | #include "tp_gc.c" 15 | #include "tp_hash.c" 16 | #include "tp_number.c" 17 | #include "tp_list.c" 18 | #include "tp_dict.c" 19 | #include "tp_string.c" 20 | 21 | #include "tp_meta.c" 22 | #include "tp_data.c" 23 | #include "tp_func.c" 24 | #include "tp_frame.c" 25 | 26 | #include "tp_repr.c" 27 | 28 | #include "tpy_string.c" 29 | #include "tpy_dict.c" 30 | #include "tpy_list.c" 31 | 32 | #include "tp_vm.c" 33 | 34 | /* FIXME: after string / dict gets a meta, register these methods 35 | * to the meta in tpy_builtin 36 | * and tp_ops above tpy **/ 37 | #include "tp_ops.c" 38 | #include "tp_import.c" 39 | #include "tp_interp.c" 40 | 41 | #ifdef TP_SANDBOX 42 | #include "interp/sandbox.c" 43 | #endif 44 | 45 | 46 | #include "tpy_builtins.c" 47 | -------------------------------------------------------------------------------- /java/TinyPy.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | class TinyPy { 4 | public static void main(String [] args) { 5 | init(); 6 | run("test.py"); 7 | say(getString("foo")); 8 | say(eval("say")); 9 | deinit(); 10 | } 11 | 12 | static void gdb() { 13 | System.out.println("enter number when gdb is ready"); 14 | Scanner sc = new Scanner(System.in); 15 | int intNum = sc.nextInt(); 16 | } 17 | 18 | void say2(String message) { 19 | System.out.println(message); 20 | } 21 | 22 | static void say(String message) { 23 | System.out.println(message); 24 | } 25 | 26 | static public native void init(); 27 | static public native String run(String filename); 28 | static public native String eval(String code); 29 | static public native void deinit(); 30 | 31 | static public native String getString(String name); 32 | static public native void call(); 33 | 34 | static public native long getPyObject(String name); 35 | 36 | static { 37 | System.loadLibrary("tinypy"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/test_object.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class Create: 4 | def __init__(self, **args): 5 | self.__dict__.update(args) 6 | 7 | def create_with_a(a): 8 | return Create(a=a) 9 | create_with_a = staticmethod(create_with_a) 10 | 11 | def create_with_b(b): 12 | return Create(b=b) 13 | create_with_b = staticmethod(create_with_b) 14 | 15 | # This is not our canonical way of defining a class. 16 | class New: 17 | # __new__ is an implicitly classmethod. 18 | def __new__(kls, a): 19 | # be aware that we currently need to pass the args to __init__. 20 | self = object.__new__(kls, 3) 21 | self.a = a 22 | return self 23 | 24 | def __init__(self, b): 25 | self.b = b 26 | 27 | class MyTest(UnitTest): 28 | 29 | def test_new(self): 30 | obj = New(3) 31 | assert obj.a == 3 32 | assert obj.b == 3 33 | 34 | def test_create(self): 35 | obj = Create.create_with_a(3) 36 | assert obj.a == 3 37 | obj = Create.create_with_b(3) 38 | assert obj.b == 3 39 | 40 | t = MyTest() 41 | 42 | t.run() 43 | -------------------------------------------------------------------------------- /tinypy/runtime/types.py: -------------------------------------------------------------------------------- 1 | StringType = getmeta("") 2 | ListType = getmeta([]) 3 | DictType = getmeta({}) 4 | 5 | class Exception: 6 | def __init__(self, *args): 7 | self.args = args 8 | def __repr__(self): 9 | return str(self.args) 10 | 11 | class ImportError(Exception): 12 | pass 13 | 14 | def startswith(self, prefix): 15 | return self.find(prefix) == 0 16 | 17 | StringType['startswith'] = startswith 18 | 19 | def format(s, d): 20 | r = [] 21 | i = 0 22 | j = 0 23 | n = len(s) 24 | while i < n: 25 | if s[i] == '{': 26 | r.append(s[j:i]) 27 | j = i 28 | while j < n: 29 | if s[j] == '}': 30 | j = j + 1 31 | break 32 | j = j + 1 33 | 34 | spec = s[i+1:j-1] 35 | #name, fmt = spec.split(':') 36 | foo = d[spec] # eval requires the compiler: eval(spec, d) 37 | #foo = spec 38 | # print('foo', foo, spec, d) 39 | r.append(str(foo)) 40 | i = j - 1 41 | i = i + 1 42 | r.append(s[j:i]) 43 | return ''.join(r) 44 | 45 | StringType['format'] = format 46 | -------------------------------------------------------------------------------- /tinypy/tp_ops.h: -------------------------------------------------------------------------------- 1 | tp_obj tp_copy(TP, tp_obj obj); 2 | void tp_set(TP, tp_obj, tp_obj, tp_obj); 3 | int tp_iget(TP,tp_obj *r, tp_obj self, tp_obj k); 4 | 5 | tp_obj tp_mget(TP, tp_obj, tp_obj); 6 | tp_obj tp_get(TP, tp_obj, tp_obj); 7 | tp_obj tp_has(TP, tp_obj self, tp_obj k); 8 | tp_obj tp_len(TP, tp_obj); 9 | tp_obj tp_call(TP, tp_obj func, tp_obj lparams, tp_obj dparams); 10 | tp_obj tp_iter(TP, tp_obj self, tp_obj k); 11 | 12 | void tp_del(TP, tp_obj, tp_obj); 13 | tp_obj tp_str(TP, tp_obj); 14 | tp_obj tp_repr(TP, tp_obj); 15 | int tp_true(TP, tp_obj); 16 | int tp_equal(TP, tp_obj, tp_obj); 17 | int tp_lessthan(TP, tp_obj, tp_obj); 18 | tp_obj tp_add(TP,tp_obj a, tp_obj b) ; 19 | tp_obj tp_mul(TP, tp_obj a, tp_obj b); 20 | int tp_hash(TP, tp_obj v); 21 | 22 | tp_obj tp_bitwise_and(TP,tp_obj a, tp_obj b) ; 23 | tp_obj tp_bitwise_or(TP,tp_obj a, tp_obj b) ; 24 | tp_obj tp_bitwise_xor(TP,tp_obj a, tp_obj b) ; 25 | tp_obj tp_lsh(TP,tp_obj a, tp_obj b) ; 26 | tp_obj tp_rsh(TP,tp_obj a, tp_obj b) ; 27 | tp_obj tp_sub(TP,tp_obj a, tp_obj b) ; 28 | tp_obj tp_mod(TP,tp_obj a, tp_obj b) ; 29 | tp_obj tp_div(TP,tp_obj a, tp_obj b) ; 30 | tp_obj tp_pow(TP,tp_obj a, tp_obj b) ; 31 | tp_obj tp_bitwise_not(TP, tp_obj); 32 | 33 | void tp_assert(TP, tp_obj r, tp_obj b, tp_obj c); 34 | -------------------------------------------------------------------------------- /tinypy/tp_internal.h: -------------------------------------------------------------------------------- 1 | 2 | /* assert macros: 3 | * http://www.pixelbeat.org/programming/gcc/static_assert.html 4 | */ 5 | 6 | #define ASSERT_CONCAT_(a, b) a##b 7 | #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) 8 | /* These can't be used after statements in c89. */ 9 | #ifdef __COUNTER__ 10 | #define STATIC_ASSERT(e,m) \ 11 | ;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) } 12 | #else 13 | /* This can't be used twice on the same line so ensure if using in headers 14 | * that the headers are not included twice (by wrapping in #ifndef...#endif) 15 | * Note it doesn't cause an issue when used on same line of separate modules 16 | * compiled with gcc -combine -fwhole-program. */ 17 | #define STATIC_ASSERT(e,m) \ 18 | ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!!(e)) } 19 | #endif 20 | 21 | tp_inline static int _tp_min(int a, int b) { return (ab?a:b); } 23 | tp_inline static tpd_frame * tp_get_frame(TP, int i) { return TPD_FRAME(tp->frames->items[i]); } 24 | tp_inline static tpd_frame * tp_get_cur_frame(TP) { return tp_get_frame(tp, tp->frames->len - 1); } 25 | 26 | /* Detect unintended size changes. Update as needed. */ 27 | STATIC_ASSERT(sizeof(tpd_code) == 4, "size of tpd_code must be 4"); 28 | -------------------------------------------------------------------------------- /examples/julia.py: -------------------------------------------------------------------------------- 1 | # center: 10043 2 | import pygame, sys 3 | if not "tinypy" in sys.version: 4 | import pygame.locals 5 | 6 | SW,SH = 120,120 7 | 8 | def julia(s,ca,cb): 9 | pal = [((min(255,v)),(min(255,v*3/2)),(min(255,v*2))) for v in range(0,256)] 10 | for y in range(0,SH): 11 | for x in range(0,SW): 12 | i=0 13 | a=((float(x)/SW) * 4.0 - 2.0) 14 | b=((float(y)/SH) * 4.0 - 2.0) 15 | while i < 15 and (a*a)+(b*b)<4.0: 16 | na=(a*a)-(b*b)+ca 17 | nb=(2.0*a*b)+cb 18 | a=na 19 | b=nb 20 | i = i +1 21 | s.set_at((x,y),pal[i*16]) 22 | 23 | def main(): 24 | pygame.init() 25 | s = pygame.display.set_mode((SW,SH),0,32) 26 | _quit = False 27 | while not _quit: 28 | for e in pygame.event.get(): 29 | if e.type in (pygame.locals.QUIT,pygame.locals.KEYDOWN): 30 | _quit = True 31 | 32 | x,y = pygame.mouse.get_pos() 33 | ca=((float(x)/SW) * 2.0 - 1.0) 34 | cb=((float(y)/SH) * 2.0 - 1.0) 35 | ticks = pygame.time.get_ticks() 36 | julia(s,ca,cb) 37 | print(pygame.time.get_ticks()-ticks) 38 | pygame.display.flip() 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /modules/dl/repl.py: -------------------------------------------------------------------------------- 1 | import dl 2 | 3 | old_str = BUILTINS['str'] 4 | 5 | def repr(value, seen=[]): 6 | if istype(value, "string"): 7 | return "'" + my_str(value, seen).replace("'", "\\'") + "'" 8 | return my_str(value, seen) 9 | 10 | def my_str(value, seen=[]): 11 | if value in seen: 12 | return '...' 13 | seen.append(value) 14 | if istype(value, "list"): 15 | return '[' + ', '.join([repr(v, seen) for v in value]) + ']' 16 | elif istype(value, "dict"): 17 | return '{' + ', '.join([repr(k, seen) + ': ' + repr(value[k], seen) for k in value]) + '}' 18 | return old_str(value) 19 | 20 | def globals(): 21 | return BUILTINS['__globals__'] 22 | 23 | BUILTINS['__globals__'] = {} 24 | BUILTINS['globals'] = globals 25 | BUILTINS['str'] = my_str 26 | BUILTINS['repr'] = repr 27 | 28 | fgets = dl.load('', 'fgets', 'S', 'SI*') 29 | puts = dl.load('', 'printf', 'V', 'S') 30 | handle = dl.open('') 31 | stdin = dl.sym(handle, '_IO_2_1_stdin_') 32 | 33 | data = " " * 1024 34 | 35 | while True: 36 | puts('> ') 37 | source = fgets(data, len(data), stdin) 38 | if source is None: 39 | break 40 | if source.strip() == '': 41 | continue 42 | try: 43 | result = eval(source, globals()) 44 | print(str(result)) 45 | except: 46 | print(dl.exception()) 47 | print('bye') 48 | 49 | -------------------------------------------------------------------------------- /tinypy/tp_import.c: -------------------------------------------------------------------------------- 1 | tp_obj tp_import(TP, tp_obj name, tp_obj code, tp_obj fname) { 2 | tp_obj g; 3 | 4 | g = tp_object(tp); 5 | tp_set(tp, g, tp_string_atom(tp, "__name__"), name); 6 | tp_set(tp, g, tp_string_atom(tp, "__file__"), fname); 7 | tp_set(tp, g, tp_string_atom(tp, "__code__"), code); 8 | tp_set(tp, g, tp_string_atom(tp, "__dict__"), g); 9 | 10 | tp_set(tp, tp->modules, name, g); 11 | 12 | /* an older versoin of the code does not run frame of jmp == 0. Why?*/ 13 | /* 14 | tp_enter_frame(tp, tp_None, globals, code, &r); 15 | if (!tp->jmp) { 16 | tp_run_frame(tp); 17 | } 18 | * */ 19 | tp_exec(tp, code, g); 20 | 21 | return g; 22 | } 23 | 24 | /* Function: tp_import 25 | * Imports a module. 26 | * 27 | * Parameters: 28 | * fname - The filename of a file containing the module's code. 29 | * name - The name of the module. 30 | * codes - The module's code. If this is given, fname is ignored. 31 | * len - The length of the bytecode. 32 | * 33 | * Returns: 34 | * The module object. 35 | */ 36 | tp_obj tp_import_from_buffer(TP, const char * fname, const char * name, void *codes, int len) { 37 | tp_obj f = fname?tp_string_atom(tp, fname):tp_None; 38 | tp_obj bc = codes?tp_string_from_const(tp, (const char*)codes, len):tp_None; 39 | return tp_import(tp, tp_string_atom(tp, name), bc, f); 40 | } 41 | -------------------------------------------------------------------------------- /tinypy/repl.c: -------------------------------------------------------------------------------- 1 | #define CPYTHON_MOD 2 | 3 | #include "tp.c" 4 | 5 | #include 6 | #include 7 | 8 | /* Compile with: "gcc -o repl tinypy/repl.c -lm -lreadline -Wall" 9 | Supports basic one-line instructions. No module included. 10 | */ 11 | 12 | tp_obj run_protected(TP, char* source, tp_obj globals) { 13 | if(setjmp(tp->nextexpr)) { 14 | --(tp->cur); 15 | tp_print_stack(tp); 16 | return tp_None; 17 | } 18 | return tp_eval(tp, source, globals); 19 | } 20 | 21 | int main(int argc, char *argv[]) { 22 | char* line; 23 | 24 | using_history(); 25 | stifle_history(100); 26 | read_history(".tinypy_history"); 27 | 28 | tp_vm *tp = tp_init(argc,argv); 29 | tp_obj globals = tp_dict(tp); 30 | tp->echo("Tinypy REPL.\n", -1); 31 | while(NULL != (line = readline("> "))) { 32 | if(!strcmp(line, "quit")) { 33 | break; 34 | } else if(!strcmp(line, "globals")) { 35 | tp_echo(tp, tp_str(tp, globals)); 36 | tp->echo("\n", -1); 37 | continue; 38 | } 39 | add_history (line); 40 | write_history(".tinypy_history"); 41 | tp_obj result = run_protected(tp, line, globals); 42 | tp_echo(tp, result); 43 | tp->echo("\n", -1); 44 | free(line); 45 | } 46 | tp_deinit(tp); 47 | return(0); 48 | } 49 | 50 | /**/ 51 | -------------------------------------------------------------------------------- /tinypy/tp_data.c: -------------------------------------------------------------------------------- 1 | 2 | /* Function: tp_data 3 | * Creates a new data object. 4 | * 5 | * Parameters: 6 | * magic - An integer number associated with the data type. This can be used 7 | * to check the type of data objects. 8 | * v - A pointer to user data. Only the pointer is stored in the object, 9 | * you keep all responsibility for the data it points to. 10 | * 11 | * 12 | * Returns: 13 | * The new data object. 14 | * 15 | * Public fields: 16 | * The following fields can be access in a data object: 17 | * 18 | * magic - An integer number stored in the object. 19 | * val - The data pointer of the object. 20 | * info->free - If not NULL, a callback function called when the object gets 21 | * destroyed. 22 | * 23 | * Example: 24 | * > void *__free__(TP, tp_obj self) 25 | * > { 26 | * > free(self.data.val); 27 | * > } 28 | * > 29 | * > tp_obj my_obj = tpy_data(TP, 0, my_ptr); 30 | * > my_TPD_DATA(obj)->free = __free__; 31 | */ 32 | tp_obj tp_data_t(TP, int magic, void *v) { 33 | tp_obj r = {TP_DATA}; 34 | r.info = (tpd_data*)tp_malloc(tp, sizeof(tpd_data)); 35 | r.ptr = v; 36 | r.type.magic = magic; 37 | return tp_track(tp,r); 38 | } 39 | 40 | /* creates an untracked tp_data */ 41 | tp_obj tp_data_nt(TP, int magic, void *v) { 42 | tp_obj r = {TP_DATA}; 43 | r.info = NULL; 44 | r.ptr = v; 45 | r.type.magic = magic; 46 | return r; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tests/test_list.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class MyTest(UnitTest): 4 | 5 | def test_lessthan(self): 6 | assert [1, 2] < [2] 7 | assert [1, 2] <= [2] 8 | assert [1] < [2] 9 | assert [1] <= [2] 10 | assert [] < [1] 11 | 12 | def test_greaterthan(self): 13 | assert [2] > [1] 14 | assert [1, 2] > [1] 15 | assert [1, 2] >= [1] 16 | assert [1, 2] >= [1, 2] 17 | assert [2] > [] 18 | 19 | def test_equal(self): 20 | assert [1] == [1] 21 | assert [1, 2] == [1, 2] 22 | assert [] == [] 23 | 24 | # FIXME: 25 | # As we don't have an iterable type, there is not much point 26 | # to define min and max. 27 | # We shall probably remove min and max from builtins. 28 | def test_max(self): 29 | assert max(1, 2, 3) == 3 30 | assert max(3, 1, 2) == 3 31 | assert max(3, 1, 3) == 3 32 | 33 | def test_min(self): 34 | assert min(1, 2, 3) == 1 35 | assert min(3, 1, 2) == 1 36 | assert min(2, 1, 1) == 1 37 | 38 | def test_slice(self): 39 | # FIXME: support 1:2 and 1:2:1 40 | assert [0, 1, 2, 3][1, 2] == [1] 41 | assert [0, 1, 2, 3][1, None] == [1, 2, 3] 42 | assert [0, 1, 2, 3][None, None] == [0, 1, 2, 3] 43 | assert [0, 1, 2, 3][None, 1] == [0] 44 | assert [0, 1, 2, 3][None, 2] == [0, 1] 45 | 46 | t = MyTest() 47 | 48 | t.run() 49 | -------------------------------------------------------------------------------- /tinypy/tp_hash.c: -------------------------------------------------------------------------------- 1 | /* File: Dict 2 | * Functions for dealing with dictionaries. 3 | */ 4 | int tpd_lua_hash(void const *v,int l) { 5 | int i,step = (l>>5)+1; 6 | int h = l + (l >= 4?*(int*)v:0); 7 | for (i=l; i>=step; i-=step) { 8 | h = h^((h<<5)+(h>>2)+((unsigned char *)v)[i-1]); 9 | } 10 | return h; 11 | } 12 | 13 | int tp_hash(TP, tp_obj v) { 14 | switch (v.type.typeid) { 15 | case TP_NONE: return 0; 16 | case TP_NUMBER: { 17 | switch(v.type.magic) { 18 | case TP_NUMBER_INT: 19 | return tpd_lua_hash(&v.nint, sizeof(v.nint)); 20 | case TP_NUMBER_FLOAT: 21 | return tpd_lua_hash(&v.nfloat, sizeof(v.nfloat)); 22 | default: 23 | abort(); 24 | } 25 | } 26 | case TP_STRING: return tpd_lua_hash(tp_string_getptr(v), tp_string_len(v)); 27 | case TP_DICT: return tpd_lua_hash(&v.info, sizeof(void*)); 28 | case TP_LIST: { 29 | int r = TPD_LIST(v)->len; 30 | int n; 31 | for(n=0; nlen; n++) { 32 | tp_obj vv = TPD_LIST(v)->items[n]; 33 | r += (vv.type.typeid != TP_LIST)? 34 | tp_hash(tp, vv) 35 | : tpd_lua_hash(&vv.info, sizeof(void*)); 36 | } 37 | return r; 38 | } 39 | case TP_FUNC: return tpd_lua_hash(&v.info, sizeof(void*)); 40 | case TP_DATA: return tpd_lua_hash(&v.ptr, sizeof(void*)); 41 | } 42 | tp_raise(0, tp_string_atom(tp, "(tp_hash) TypeError: value unhashable")); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /tinypy/tpy_list.c: -------------------------------------------------------------------------------- 1 | 2 | /****** 3 | * Functions below take arguments from the current python scope. 4 | * */ 5 | 6 | tp_obj tpy_list_index(TP) { 7 | tp_obj self = TP_PARAMS_OBJ(); 8 | tp_obj v = TP_PARAMS_OBJ(); 9 | int i = tpd_list_find(tp, TPD_LIST(self), v, tp_equal); 10 | if (i < 0) { 11 | tp_raise(tp_None,tp_string_atom(tp, "(tp_index) ValueError: list.index(x): x not in list")); 12 | } 13 | return tp_int(i); 14 | } 15 | 16 | tp_obj tpy_list_append(TP) { 17 | tp_obj self = TP_PARAMS_OBJ(); 18 | tp_obj v = TP_PARAMS_OBJ(); 19 | tpd_list_append(tp, TPD_LIST(self), v); 20 | return tp_None; 21 | } 22 | 23 | tp_obj tpy_list_pop(TP) { 24 | tp_obj self = TP_PARAMS_OBJ(); 25 | return tpd_list_pop(tp, TPD_LIST(self), TPD_LIST(self)->len-1, "pop"); 26 | } 27 | 28 | tp_obj tpy_list_insert(TP) { 29 | tp_obj self = TP_PARAMS_OBJ(); 30 | int n = TP_PARAMS_INT(); 31 | tp_obj v = TP_PARAMS_OBJ(); 32 | tpd_list_insert(tp, TPD_LIST(self), n, v); 33 | return tp_None; 34 | } 35 | 36 | 37 | tp_obj tpy_list_extend(TP) { 38 | tp_obj self = TP_PARAMS_TYPE(TP_LIST); 39 | tp_obj v = TP_PARAMS_TYPE(TP_LIST); 40 | tpd_list_extend(tp, TPD_LIST(self), TPD_LIST(v)); 41 | return tp_None; 42 | } 43 | 44 | 45 | /* FIXME: add tpd interface. */ 46 | int _tp_list_sort_cmp(tp_obj *a, tp_obj *b) { 47 | if(tp_equal(0, *a, *b)) { 48 | return 0; 49 | } 50 | if (tp_lessthan(0, *a, *b)) { 51 | return -1; 52 | } 53 | return 1; 54 | } 55 | 56 | tp_obj tpy_list_sort(TP) { 57 | tp_obj self = TP_PARAMS_OBJ(); 58 | qsort(TPD_LIST(self)->items, TPD_LIST(self)->len, sizeof(tp_obj), (int(*)(const void*,const void*))_tp_list_sort_cmp); 59 | return tp_None; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Tinypy 2 | ====== 3 | 4 | .. image:: https://github.com/rainwoodman/tinypy/workflows/main/badge.svg 5 | :target: https://github.com/rainwoodman/tinypy/actions?query=workflow%3Amain 6 | 7 | This is a refactored version of tinypy. 8 | 9 | The focus is on the core language, and production readiness features 10 | (testing, code abstraction). 11 | 12 | The work is in-progress. Expect bugs. 13 | 14 | 15 | See README.txt.old for a description of the original tinypy. 16 | 17 | build 18 | ----- 19 | 20 | To build this version of tinypy, you will need a Python (2/3) to bootstrap 21 | the compiler. 22 | 23 | .. code:: 24 | 25 | make 26 | 27 | To run the (incomplete) test suite: 28 | 29 | .. code:: 30 | 31 | make test 32 | 33 | # stop on first failure 34 | make xtest 35 | 36 | # run full gc every step. 37 | make test-dbg 38 | 39 | run 40 | --- 41 | 42 | To run a script: 43 | 44 | .. code:: 45 | 46 | ./tpy tests/test_str.py 47 | 48 | 49 | To see the disassembly: 50 | 51 | .. code:: 52 | 53 | ./tpc -d tests/test_str.py 54 | 55 | execution pipeline 56 | ------------------ 57 | 58 | Compiler: 59 | source code -> tokenize -> parse -> encode -> tpc 60 | 61 | VM: 62 | tpc -> tpvm 63 | 64 | A module can be imported only if it is already embedded. 65 | 66 | 'site' modules are in modules/ (may be broken as there are no test coverages). 67 | 68 | Internal runtime modules (part of the language) written in tpy are in tinypy/runtime. 69 | 70 | namespaces 71 | ---------- 72 | 73 | tinypy.compiler: the compiler (`compiler/`) 74 | 75 | tinypy.runtime: the runtime support (`runtime/*.py`) 76 | 77 | tinypy.runtime.builtins: the runtime in C (`tpy_builtins.c`). 78 | 79 | Of course things may still need to be shuffled around between these modules 80 | 81 | 82 | -------------------------------------------------------------------------------- /tinypy/printf/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mini-printf.h" 6 | 7 | int handler(void* data, void* obj, int ch, int len_hint, char **buf) 8 | { 9 | if(data != NULL) return 0; 10 | *buf = malloc(32); 11 | mini_snprintf(*buf, 32, "%cA%03dB%03dC", ch, strlen(obj), len_hint); 12 | return strlen(*buf); 13 | } 14 | 15 | void freeor(void* data, void* buf) 16 | { 17 | if(data != NULL) return; 18 | free(buf); 19 | } 20 | 21 | int main(void) 22 | { 23 | mini_printf_set_handler(NULL, handler, freeor); 24 | int n; 25 | char buff[30]; 26 | n = mini_snprintf(buff, sizeof buff, "%07d %7d %010d", 1, 2, 3); 27 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 28 | n = mini_snprintf(buff, sizeof buff, "%07lX %7lX %016lX", 0xffffffffffL, 0xffffffffffL, 0xffffffffffL); 29 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 30 | n = mini_snprintf(buff, sizeof buff, "%010O %04R", "object1", "object2"); 31 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 32 | n = mini_snprintf(buff, sizeof buff, "testing %d %d %07d", 1, 2, 3); 33 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 34 | n = mini_snprintf(buff, sizeof buff, "faster %s %ccheaper%c", "and", 34, 34); 35 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 36 | n = mini_snprintf(buff, sizeof buff, "%x %% %X", 0xdeadf00d, 0xdeadf00d); 37 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 38 | n = mini_snprintf(buff, sizeof buff, "%09d%09d%09d%09d%09d", 1, 2, 3, 4, 5); 39 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 40 | n = mini_snprintf(buff, sizeof buff, "%d %u %d %u", 50, 50, -50, -50); 41 | puts(buff); mini_snprintf(buff, sizeof buff, "%d", n); puts(buff); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tinypy/compiler/asm.py: -------------------------------------------------------------------------------- 1 | from tinypy.compiler import opcodes 2 | 3 | def prepare(x): 4 | """ Prepares the line for processing by breaking it into tokens, 5 | removing empty tokens and stripping whitespace """ 6 | try: 7 | ind = x.index('"') 8 | except: 9 | ind = -1 10 | if ind != -1: 11 | d = x[ind:] 12 | x = x[:ind] 13 | x = x.split(' ') 14 | tmp = [] 15 | final = [] 16 | for i in x: 17 | if i: 18 | if i[0] != ':': 19 | tmp.append(i) 20 | for i in tmp[:4]: 21 | final.append(i) 22 | if not d: 23 | d = "".join(tmp[4:]) 24 | final.append(d.strip()) 25 | return final 26 | 27 | def dequote(x): 28 | """ Removes outermost quotes from a string, if they exist """ 29 | if x[0] == '"' and x[len(x)-1] == '"': 30 | return x[1:len(x)-1] 31 | return x 32 | 33 | def assemble(asmc): 34 | asmc = asmc.strip() 35 | asmc = asmc.split('\n') 36 | bc = [] 37 | ops = opcodes.names 38 | for line in asmc: 39 | current = prepare(line) 40 | i,a,b,c,d = current 41 | a = int(a) 42 | b = int(b) 43 | c = int(c) 44 | bc.extend(bytes([ops[i], a, b, c])) 45 | if i == "LINE": 46 | n = a * 4 47 | d = dequote(d) 48 | text = d 49 | text += b'\0' * (n - len(d)) 50 | bc.append(text) 51 | if i == "STRING": 52 | d = dequote(d) 53 | text = d + b"\0"*(4-len(d)%4) 54 | bc.append(text) 55 | elif i == "NUMBER": 56 | d = int(d) 57 | bc.append(fpack(d)) 58 | bc = "".join(bc) 59 | return bc 60 | 61 | if __name__ == '__main__': 62 | asmc = load(ARGV[1]) 63 | bc = assemble(asmc) 64 | save(ARGV[2], bc) 65 | -------------------------------------------------------------------------------- /ROADMAP.txt: -------------------------------------------------------------------------------- 1 | tinypy is a minimalist implementation of python in 64k of code 2 | 3 | "batteries not included (yet)" 4 | "lua for people who like python" 5 | 6 | what tinypy is: 7 | * parser and bytecode compiler written in tinypy 8 | * fully bootstrapped 9 | * luaesque virtual machine with garbage collection written in C 10 | it's "stackless" sans any "stackless" features 11 | * cross-platform :) it runs under windows / linux / macosx 12 | * a fairly decent subset of python 13 | o classes and single inheritance 14 | o functions with variable or keyword arguments 15 | o strings, lists, dicts, numbers 16 | o modules, list comprehensions 17 | o exceptions with full traceback 18 | o some builtins 19 | - an easy C-API for building modules 20 | - 64k of code (for at least some definition of 64k) 21 | - interesting, educational, nifty, and useful 22 | - well tested 23 | - easy to read, maintain, and use 24 | - fun fun fun!!! 25 | - you can static compile it and its modules (MIT license, so "it's all good!") 26 | 27 | what tinypy will be: 28 | - sandboxed 29 | - a Cpython module (setup.py install) 30 | - including some batteries (math, random, re, marshal, pygame?!) 31 | - Visual Studio compatible 32 | - documented 33 | 34 | what tinypy might be: 35 | - as fast as python (maybe faster?) 36 | - including a JIT module 37 | - C89 compatible 38 | - C++ compatible (like lua) 39 | - a shed-skin module 40 | - including a dynamic loading module 41 | 42 | what tinypy won't be: 43 | - a full implementation of python 44 | - totally compatible with python 45 | 46 | alternatives to tinypy: 47 | - lua 48 | - shed-skin 49 | - pymite 50 | - pyvm 51 | - cython 52 | - pypy 53 | - jython 54 | - ironpython 55 | - python 56 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | BACKEND=tpvm 2 | XFAIL=0 3 | MEMCHECK= 4 | 5 | # Call getopt to validate the provided input. 6 | OPT=$(getopt -omxB: -l memcheck,xfail,backend: -- "$@") 7 | 8 | if [[ $? -ne 0 ]] ; then 9 | echo "Incorrect options provided" 10 | exit 1 11 | fi 12 | 13 | eval set -- "$OPT" 14 | 15 | while true; do 16 | case "$1" in 17 | -m | --memcheck) 18 | MEMCHECK="valgrind --error-exitcode=1" 19 | ;; 20 | -x | --xfail) 21 | XFAIL=1 22 | ;; 23 | -B | --backend) 24 | shift; # The arg is next in position args 25 | BACKEND=$1 26 | if [[ ! ${BACKEND} =~ tpy|tpy-dbg|tpvm|tpvm-dbg|tpy-shared ]]; then 27 | echo "Incorrect options provided. Use tpy, tpy-dbg, tpvm, tpvm-dbg, or tpy-shared" 28 | exit 1 29 | fi 30 | ;; 31 | --) 32 | shift 33 | break 34 | ;; 35 | esac 36 | shift 37 | done 38 | 39 | shift $((OPTIND -1)) 40 | 41 | TESTS=$@ 42 | echo $TESTS 43 | 44 | TPC=./tpc 45 | 46 | function run { 47 | tpc=${1//.py/.tpc} 48 | if [[ "${tpc}" == "$1" ]]; then 49 | echo $1 does not end with .py 50 | exit 1 51 | fi 52 | if [[ "${BACKEND}" =~ tpvm|tpvm-dbg ]]; then 53 | echo "${TPC} -o ${tpc} $1" 54 | echo "./${BACKEND} ${tpc}" 55 | "${TPC}" -o ${tpc} $1 || return 1 56 | ${MEMCHECK} "./${BACKEND}" ${tpc} || return 1 57 | else 58 | echo "./${BACKEND} $1" 59 | ${MEMCHECK} "./${BACKEND}" $1 || return 1 60 | fi 61 | } 62 | 63 | st=0 64 | for i in ${TESTS[@]}; do 65 | if ! run "${i}"; then 66 | echo ==== ${st} 67 | if [[ ${XFAIL} -ne 0 ]]; then 68 | exit 255 69 | fi 70 | st=1 71 | fi 72 | done 73 | 74 | if [ "${st}" -ne 0 ]; then 75 | echo Some tests failed. 76 | exit 255 77 | else 78 | echo All tests passed. 79 | fi 80 | -------------------------------------------------------------------------------- /tests/test_attr_magics.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class MyClass: 4 | def __init__(self): 5 | self.__dict__['internal'] = {} 6 | 7 | def __set__(self, k, v): 8 | self.internal[k] = v 9 | 10 | def f(self): 11 | return 3 12 | 13 | def __get__(self, k): 14 | return k 15 | 16 | class MyBypass: 17 | def __set__(self, k, v): 18 | return True 19 | 20 | class MyDerived(MyClass): 21 | def __init__(self): 22 | self.__dict__['derived'] = True 23 | MyClass.__init__(self) 24 | 25 | class MyTest(UnitTest): 26 | 27 | def test_rawdict(self): 28 | # __dict__ is the raw dict 29 | obj = MyClass() 30 | r1 = obj.__dict__ 31 | r2 = getraw(obj) 32 | r1['foo'] = 1234 33 | assert r2['foo'] == 1234 34 | assert obj['foo'] == 1234 35 | 36 | def test_get_set(self): 37 | # get / set override logic 38 | 39 | # get occurs after default lookup 40 | # set occurs before default lookup 41 | 42 | obj = MyClass() 43 | obj.set_attr = '1234' 44 | obj.internal = 'foo' 45 | assert obj.f() == 3 46 | assert obj.unset_attr == "unset_attr" 47 | assert obj.internal['set_attr'] == '1234' 48 | assert obj.internal['internal'] == 'foo' 49 | assert obj.set_attr == "set_attr" 50 | 51 | def test_bypass(self): 52 | obj = MyBypass() 53 | obj.bypass = 300 54 | assert obj.__dict__['bypass'] == 300 55 | assert obj.bypass == 300 56 | 57 | def test_derived(self): 58 | obj = MyDerived() 59 | obj.set_attr = '1234' 60 | obj.internal = 'foo' 61 | 62 | assert obj.derived == True 63 | assert obj.unset_attr == "unset_attr" 64 | assert obj.internal['set_attr'] == '1234' 65 | assert obj.internal['internal'] == 'foo' 66 | assert obj.set_attr == "set_attr" 67 | 68 | t = MyTest() 69 | 70 | t.run() 71 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | # main test workflow; ported from .travis.yaml 2 | 3 | name: main 4 | 5 | on: 6 | push: 7 | branches: [ '*', $default-branch ] 8 | pull_request: 9 | branches: [ $default-branch ] 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | include: 17 | - { python-version: 3.8, backend: "tpy-shared", } 18 | - { python-version: 3.8, backend: "tpvm", } 19 | - { python-version: 3.8, backend: "tpy", } 20 | # memory checks 21 | - { python-version: 3.8, backend: "tpy", memcheck: "--memcheck" } 22 | - { python-version: 3.8, backend: "tpvm-dbg", memcheck: "--memcheck" } 23 | # A 2.7 basic test, mostly for compiling the compiler with py-2.7 24 | - { python-version: 2.7, backend: "tpy", } 25 | # A m32 basic test 26 | - { python-version: 3.8, backend: "tpy", arch: "-m32" } 27 | # A mx32 basic test 28 | - { python-version: 3.8, backend: "tpy", arch: "-mx32" } 29 | 30 | steps: 31 | - name: Checkout source code 32 | uses: actions/checkout@v2 33 | 34 | - name: Set up Bootstrap Python ${{ matrix.python-version }} 35 | uses: actions/setup-python@v2 36 | with: 37 | python-version: ${{ matrix.python-version }} 38 | 39 | - name: Install Valgrind 40 | run: | 41 | sudo apt-get update 42 | sudo apt-get install -y valgrind 43 | if: matrix.memcheck 44 | 45 | - name: Install GCC multilib 46 | run: | 47 | sudo apt-get update 48 | sudo apt-get install -y gcc-multilib 49 | if: matrix.arch 50 | 51 | - name: Build 52 | run: | 53 | make all CFLAGS=${{ matrix.arch }} 54 | make shared CFLAGS=${{ matrix.arch }} 55 | make debug CFLAGS=${{ matrix.arch }} 56 | 57 | - name: Test 58 | run: | 59 | bash run-tests.sh --backend=${{ matrix.backend }} ${{ matrix.memcheck }} tests/*.py 60 | -------------------------------------------------------------------------------- /tinypy/compiler/opcodes.py: -------------------------------------------------------------------------------- 1 | EOF = 0 2 | 3 | ADD = 1 4 | SUB = 2 5 | MUL = 3 6 | DIV = 4 7 | POW = 5 8 | MOD = 40 9 | LSH = 41 10 | RSH = 42 11 | 12 | BITAND = 6 13 | BITOR = 7 14 | BITXOR = 46 15 | NOT = 48 16 | BITNOT = 49 17 | 18 | MOVE = 16 19 | MGET = 9 20 | GET = 10 21 | SET = 11 22 | ITER = 43 23 | 24 | LEN = 30 25 | GGET = 14 26 | GSET = 15 27 | IGET = 33 28 | DEL = 44 29 | UPDATE = 50 30 | 31 | DEF = 17 32 | PASS = 18 33 | 34 | JUMP = 19 35 | CALL = 20 36 | RETURN = 21 37 | IF = 22 38 | IFN = 47 39 | RAISE = 38 40 | SETJMP = 39 41 | 42 | ASSERT = 23 43 | 44 | NOTIN = 8 45 | EQ = 24 46 | LE = 25 47 | LT = 26 48 | NE = 36 49 | IN = 37 50 | 51 | PARAMS = 32 52 | NUMBER = 12 53 | STRING = 13 54 | DICT = 27 55 | LIST = 28 56 | NONE = 29 57 | CLASS = 52 58 | 59 | LINE = 31 60 | FILE = 34 61 | NAME = 35 62 | 63 | REGS = 45 64 | VAR = 51 65 | 66 | def _make_dicts(): 67 | names = {} 68 | codes = {} 69 | G = globals() 70 | for k in G: 71 | if not (k[0] >= 'A' and k[0] <= 'Z'): 72 | continue 73 | names[G[k]] = k 74 | if k in codes: 75 | raise Exception("duplicated codes") 76 | codes[k] = G[k] 77 | return names, codes 78 | 79 | names, codes = _make_dicts() 80 | 81 | def create_ccode(): 82 | lines = [] 83 | cases = [] 84 | 85 | for name in codes: 86 | cval = codes[name] 87 | cname = "TP_I" + name 88 | lines.append("#define " + cname + " " + str(cval) + " ") 89 | cases.append("case " + cname + ': return "' + name + '";') 90 | 91 | cases.append("default : return NULL;"); 92 | header = "/* Generated from opcodes.py with tpc -x. Do not modify. */\n" 93 | enums = '\n'.join(lines) 94 | translate = ("char * tp_get_opcode_name(int opcode) {\n" 95 | + "switch(opcode) {\n" 96 | + '\n'.join(cases) 97 | + "\n}\n}\n" 98 | ) 99 | return '\n'.join([header, enums, translate]) 100 | -------------------------------------------------------------------------------- /tinypy/tp_param.c: -------------------------------------------------------------------------------- 1 | /* Function: tp_params 2 | * Initialize the tinypy parameters. 3 | * 4 | * When you are calling a tinypy function, you can use this to initialize the 5 | * list of parameters getting passed to it. Usually, you may want to use 6 | * or . 7 | * 8 | * In a C-API function, always finish 'parsing' the params before calling other 9 | * tpy functions or tp_exec (which may eventually call other tpy functions). 10 | */ 11 | tp_obj tp_params(TP) { 12 | *tp->lparams = tp_list_t(tp); 13 | return *tp->lparams; 14 | } 15 | 16 | /* Function: tp_params_n 17 | * Specify a list of objects as function call parameters. 18 | * 19 | * See also: , 20 | * 21 | * Parameters: 22 | * n - The number of parameters. 23 | * argv - A list of n tinypy objects, which will be passed as parameters. 24 | * 25 | * Returns: 26 | * The parameters list. You may modify it before performing the function call. 27 | */ 28 | tp_obj tp_params_n(TP,int n, tp_obj argv[]) { 29 | tp_obj r = tp_params(tp); 30 | int i; 31 | for (i=0; i= ord("A") and ord(x) <= ord("Z") 6 | 7 | def pad(s, n): 8 | p = "" 9 | if n < 0: 10 | m = -n - len(s) 11 | if m > 0: p = " " * m 12 | return p + s 13 | m = n - len(s) 14 | if m > 0: p = " " * m 15 | return s + p 16 | 17 | def text(x, ip, bc): 18 | return bytes(bc[ip:ip+x]) 19 | 20 | def trim(x): 21 | txt = [] 22 | for c in x: 23 | if ord(c): 24 | txt.append(c) 25 | return "".join(txt) 26 | 27 | def ord_or_int(x): 28 | try: 29 | return ord(x) 30 | except: 31 | return int(x) 32 | 33 | def disassemble(bc): 34 | bc = [ord_or_int(x) for x in bc] 35 | asmc = [] 36 | ip = 0 37 | names = opcodes.names 38 | while ip < len(bc): 39 | i, a, b, c = bc[ip:ip + 4] 40 | line = "" 41 | line += pad(str(ip), 4) + ":" 42 | line += pad(names[i], 10) + ":" 43 | line += " " + pad(str(a), -3) 44 | line += " " + pad(str(b), -3) 45 | line += " " + pad(str(c), -3) 46 | ip += 4 47 | if i == opcodes.LINE: 48 | n = a * 4 49 | line += " " + str(text(n,ip,bc)) 50 | line = trim(line) 51 | ip += n 52 | elif i == opcodes.VAR: 53 | n = b * 256 + c 54 | line += " " + str(a) + ": " + str(text(n,ip,bc)) 55 | line = trim(line) 56 | ip += (int(n / 4) + 1) * 4 57 | elif i == opcodes.STRING: 58 | n = b * 256 + c 59 | line += " " + str(text(n,ip,bc)) 60 | line = trim(line) 61 | ip += (int(n / 4) + 1) * 4 62 | elif i == opcodes.NUMBER: 63 | f = unpack('=' + chr(b), text(c,ip,bc)) 64 | line += " " + str(f) 65 | ip += c 66 | asmc.append(line) 67 | print(line) 68 | asmc = "\n".join(asmc) 69 | return asmc 70 | 71 | if __name__ == "__main__": 72 | bc = load(ARGV[1]) 73 | asmc = disassemble(bc) 74 | print(asmc) 75 | -------------------------------------------------------------------------------- /tinypy/tp_number.h: -------------------------------------------------------------------------------- 1 | tp_inline static tp_obj tp_int(long v) { 2 | tp_obj r = {TP_NUMBER}; 3 | r.type.magic = TP_NUMBER_INT; 4 | r.nint = v; 5 | return r; 6 | } 7 | 8 | tp_inline static tp_obj tp_float(double v) { 9 | tp_obj r = {TP_NUMBER}; 10 | r.type.magic = TP_NUMBER_FLOAT; 11 | r.nfloat = v; 12 | return r; 13 | } 14 | 15 | tp_inline static tp_obj tp_number_cast(TP, tp_obj v, enum TPTypeMagic kind) { 16 | if(v.type.typeid != TP_NUMBER) { 17 | tp_raise_printf(tp_None, "tp_cast: not a number object"); 18 | } 19 | tp_obj r = {TP_NUMBER}; 20 | r.type.magic = kind; 21 | switch(v.type.magic) { 22 | case TP_NUMBER_INT: switch (kind) { 23 | case TP_NUMBER_FLOAT: r.nfloat = v.nint; break; 24 | case TP_NUMBER_INT: r.nint = v.nint; break; 25 | default: abort(); 26 | } break; 27 | case TP_NUMBER_FLOAT: switch (kind) { 28 | case TP_NUMBER_FLOAT: r.nfloat = v.nfloat; break; 29 | case TP_NUMBER_INT: r.nint = v.nfloat; break; 30 | default: abort(); 31 | } break; 32 | default: abort(); 33 | } 34 | return r; 35 | } 36 | 37 | tp_inline static enum TPTypeMagic tp_number_upcast(TP, tp_obj *a, tp_obj *b) { 38 | enum TPTypeMagic kind = a->type.magic; 39 | if(b->type.magic > kind) { 40 | kind = b->type.magic; 41 | } 42 | *a = tp_number_cast(tp, *a, kind); 43 | *b = tp_number_cast(tp, *b, kind); 44 | return kind; 45 | } 46 | 47 | tp_inline static long tp_number_as_int(TP, tp_obj v) { 48 | if(v.type.magic != TP_NUMBER_INT) { 49 | abort(); 50 | tp_raise_printf(0, "tp_number_as_int: expecting an integer"); 51 | } 52 | return v.nint; 53 | } 54 | #define TPN_AS_INT(v) tp_number_as_int(tp, v) 55 | tp_inline static double tp_number_as_float(TP, tp_obj v) { 56 | if(v.type.magic != TP_NUMBER_FLOAT) { 57 | abort(); 58 | tp_raise_printf(0, "tp_number_as_int: expecting a float"); 59 | } 60 | return v.nfloat; 61 | } 62 | #define TPN_AS_FLOAT(v) tp_number_as_float(tp, v) 63 | 64 | tp_inline static tp_obj tp_bool(int v) { 65 | return v?tp_True:tp_False; 66 | } 67 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | * added support for meta-methods (lua style): __get__, __set__, __call__ 2 | setmeta(dict,meta) getmeta(dict) getraw(dict) -> a dict that won't check 3 | its meta-methods 4 | * added some API testing to tests.py 5 | * applied OSX setup.py patch by Atul Varma 6 | * fixed to issue #14 by Dean 7 | * updated meta-method patch to be more pythonic 8 | * resolved Issue 19-Multiplying a string with a negative value crashes tinypy 9 | by Denis 10 | 11 | == 1.1 ========================================================================= 12 | * applied patch by Dean Hall to fix several range() bugs 13 | * applied patch by Krzysztof Kowalczyk to add VS 2005, 2008 compatibility. 14 | * reorganized the source so that contributed modules can go into contrib 15 | * reorganized the reorganization so that modules go into modules 16 | since that seems less confusing somehow 17 | * added a crude pygame module - to see it in action: 18 | $ python setup.py linux pygame 19 | $ ./build/tinypy examples/julia.py 20 | * added support for 21 | from x import * 22 | from x import y 23 | * trimmed off 1064 bytes by changing all the tokens from dicts to 24 | a Token class in tokenize, parse, encode modules 25 | * applied patch by Seth Lemons to support /* */ comment stripping in 26 | setup.py and get tinypy up to -std=c89 spec 27 | * cleaned up encode.py so that registers are no longer leaked 28 | added assert after each frame to check that this is the case 29 | * applied patch by KK to improve tinypy/tests.py feedback 30 | * applied patch by allefant to finish namespacing tinypy 31 | * applied patch by allefant to keep blob->tinypy.h from having warnings 32 | * added math module from Rockins Chen 33 | * fixed precedent of ** 34 | * removed unused tp_data meta methods 35 | * made tp_data API "safer" by requiring the magic type 36 | * improved setup.py to handle the clean argument 37 | * renamed some internal fnc names for no good reason 38 | * added boot option to setup.py so that full boostrapping 39 | isn't always required 40 | * applied const correctness patch from allefant 41 | * fixed memory leak in list & dict copy functions 42 | * improved kwargs support 43 | * got it compiling with -Wc++-compat 44 | * applied ord patch from allefant 45 | * fixed mingw build -------------------------------------------------------------------------------- /tinypy/tp_number.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | tp_obj tp_pack(TP, char *format, tp_obj v) { 4 | tp_obj r; 5 | if(*format != '=') { 6 | tp_raise_printf(tp_None, "Only = format is supported."); 7 | } 8 | format ++; 9 | switch(*format) { 10 | case 'd': 11 | r = tp_string_t(tp,sizeof(double)); 12 | *(double*) tp_string_getptr(r) = TPN_AS_FLOAT(v); 13 | break; 14 | case 'i': 15 | r = tp_string_t(tp,sizeof(int32_t)); 16 | *(int32_t*) tp_string_getptr(r) = TPN_AS_INT(v); 17 | break; 18 | case 'I': 19 | r = tp_string_t(tp,sizeof(uint32_t)); 20 | *(uint32_t*) tp_string_getptr(r) = TPN_AS_INT(v); 21 | break; 22 | case 'q': 23 | r = tp_string_t(tp,sizeof(int64_t)); 24 | *(int64_t*) tp_string_getptr(r) = TPN_AS_INT(v); 25 | break; 26 | case 'Q': 27 | r = tp_string_t(tp,sizeof(uint64_t)); 28 | *(uint64_t*) tp_string_getptr(r) = TPN_AS_INT(v); 29 | break; 30 | default: 31 | abort(); 32 | } 33 | return r; 34 | } 35 | tp_obj tp_unpack(TP, char * format, tp_obj v) { 36 | tp_obj r; 37 | if(*format != '=') { 38 | tp_raise_printf(tp_None, "Only = format is supported."); 39 | } 40 | format ++; 41 | switch(*format) { 42 | case 'd': 43 | if (tp_string_len(v) != sizeof(double)) goto ex_len; 44 | return tp_float(*((double*) tp_string_getptr(v))); 45 | case 'i': 46 | if (tp_string_len(v) != sizeof(int32_t)) goto ex_len; 47 | return tp_int(*((int32_t*) tp_string_getptr(v))); 48 | case 'I': 49 | if (tp_string_len(v) != sizeof(uint32_t)) goto ex_len; 50 | return tp_int(*((uint32_t*) tp_string_getptr(v))); 51 | case 'q': 52 | if (tp_string_len(v) != sizeof(int64_t)) goto ex_len; 53 | return tp_int(*((int64_t*) tp_string_getptr(v))); 54 | case 'Q': 55 | if (tp_string_len(v) != sizeof(uint64_t)) goto ex_len; 56 | return tp_int(*((uint64_t*) tp_string_getptr(v))); 57 | default: 58 | abort(); 59 | } 60 | return r; 61 | 62 | ex_len: 63 | tp_raise(tp_None, tp_string_atom(tp, "unpack ValueError: length of string is incorrect.")); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /tinypy/tp_func.c: -------------------------------------------------------------------------------- 1 | tp_obj tp_call_extern(TP, tp_obj func) { 2 | } 3 | 4 | tp_obj tp_func_nt(TP, int t, void *ptr) { 5 | tp_obj r = {TP_FUNC}; 6 | tpd_func *info = (tpd_func*)tp_malloc(tp, sizeof(tpd_func)); 7 | r.type.mask = t; 8 | r.info = info; 9 | r.ptr = ptr; 10 | return r; 11 | } 12 | 13 | tp_obj tp_func_t(TP, int t, void *ptr) { 14 | return tp_track(tp, tp_func_nt(tp, t, ptr)); 15 | } 16 | 17 | tp_obj tp_bind(TP, tp_obj function, tp_obj self) { 18 | tp_obj r = tp_func_nt(tp, 19 | function.type.mask | TP_FUNC_MASK_METHOD, 20 | function.ptr); 21 | memcpy(r.info, function.info, sizeof(*TPD_FUNC(function))); 22 | TPD_FUNC(r)->instance = self; 23 | return tp_track(tp, r); 24 | } 25 | 26 | tp_obj tp_staticmethod(TP, tp_obj function) { 27 | tp_obj r = tp_func_nt(tp, 28 | function.type.mask | TP_FUNC_MASK_STATIC, 29 | function.ptr); 30 | memcpy(r.info, function.info, sizeof(*TPD_FUNC(function))); 31 | TPD_FUNC(r)->instance = tp_None; 32 | return tp_track(tp, r); 33 | } 34 | 35 | tp_obj tp_def(TP, tp_obj code, tp_obj g, 36 | tp_obj args, 37 | tp_obj defaults, 38 | tp_obj varargs, 39 | tp_obj varkw) { 40 | tp_obj r = tp_func_t(tp, 0, NULL); 41 | TPD_FUNC(r)->code = code; 42 | TPD_FUNC(r)->globals = g; 43 | TPD_FUNC(r)->instance = tp_None; 44 | TPD_FUNC(r)->args = args; 45 | TPD_FUNC(r)->defaults = defaults; 46 | TPD_FUNC(r)->varargs = varargs; 47 | TPD_FUNC(r)->varkw = varkw; 48 | return r; 49 | } 50 | 51 | /* Function: tp_func 52 | * Creates a new tinypy function object. 53 | * 54 | * This is how you can create a tinypy function object which, when called in 55 | * the script, calls the provided C function. 56 | */ 57 | tp_obj tp_function(TP, tp_obj v(TP)) { 58 | tp_obj r = tp_func_t(tp, 0, v); 59 | TPD_FUNC(r)->code = tp_None; 60 | TPD_FUNC(r)->globals = tp_None; 61 | TPD_FUNC(r)->instance = tp_None; 62 | TPD_FUNC(r)->args = tp_None; 63 | TPD_FUNC(r)->defaults = tp_None; 64 | TPD_FUNC(r)->varargs = tp_None; 65 | TPD_FUNC(r)->varkw = tp_None; 66 | return r; 67 | } 68 | 69 | /* Function: tp_method 70 | * Creates a method for an instance. Use this in a C module to create objects by 71 | * proto-typing. 72 | */ 73 | tp_obj tp_method(TP, tp_obj self, tp_obj v(TP)) { 74 | return tp_bind(tp, tp_function(tp, v), self); 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /tinypy/tp_list.c: -------------------------------------------------------------------------------- 1 | tp_obj tp_list_nt(TP) { 2 | tp_obj r = {TP_LIST}; 3 | r.info = tpd_list_new(tp); 4 | return r; 5 | } 6 | 7 | tp_obj tp_list_t(TP) { 8 | return tp_track(tp, tp_list_nt(tp)); 9 | } 10 | 11 | tp_obj tp_list_from_items(TP, int n, tp_obj *argv) { 12 | int i; 13 | tp_obj r = tp_list_t(tp); 14 | tpd_list_realloc(tp, TPD_LIST(r),n); 15 | for (i=0; ialloc = o->len; 41 | r->items = (tp_obj*)tp_malloc(tp, sizeof(tp_obj)*o->len); 42 | memcpy(r->items,o->items,sizeof(tp_obj)*o->len); 43 | return tp_track(tp, val); 44 | } 45 | 46 | tp_obj tp_list_add(TP, tp_obj a, tp_obj b) 47 | { 48 | tp_obj r; 49 | r = tp_list_copy(tp, a); 50 | tpd_list_extend(tp, TPD_LIST(r), TPD_LIST(b)); 51 | return r; 52 | } 53 | 54 | tp_obj tp_list_mul(TP, tp_obj a, int n) 55 | { 56 | tp_obj r; 57 | r = tp_list_copy(tp, a); 58 | int i; 59 | for (i = 1; i < n; i ++) { 60 | tpd_list_extend(tp, TPD_LIST(r), TPD_LIST(a)); 61 | } 62 | return r; 63 | } 64 | 65 | int tp_list_equal(TP, tp_obj a, tp_obj b) 66 | { 67 | int n, v; 68 | if(TPD_LIST(a)->len != TPD_LIST(b)->len) { 69 | return 0; 70 | } 71 | for(n=0; nlen; n++) { 72 | tp_obj aa = TPD_LIST(a)->items[n]; 73 | tp_obj bb = TPD_LIST(b)->items[n]; 74 | if(!tp_equal(tp, aa, bb)) return 0; 75 | } 76 | return 1; 77 | } 78 | 79 | int tp_list_lessthan(TP, tp_obj a, tp_obj b) 80 | { 81 | int n, v; 82 | for(n=0; n<_tp_min(TPD_LIST(a)->len, TPD_LIST(b)->len); n++) { 83 | tp_obj aa = TPD_LIST(a)->items[n]; 84 | tp_obj bb = TPD_LIST(b)->items[n]; 85 | if(tp_equal(tp, aa, bb)) continue; 86 | if(tp_lessthan(tp, aa, bb)) { 87 | return 1; 88 | } else { 89 | return 0; 90 | } 91 | } 92 | return TPD_LIST(a)->len < TPD_LIST(b)->len; 93 | } 94 | -------------------------------------------------------------------------------- /tinypy/tpd_list.c: -------------------------------------------------------------------------------- 1 | /* FIXME: 2 | * 3 | * This file shall not raise exceptions. 4 | * 5 | * Only tp_xxx function can raise exceptions. 6 | * */ 7 | void tpd_list_realloc(TP, tpd_list *self, int len) { 8 | if (!len) { len=1; } 9 | self->items = (tp_obj*)tp_realloc(tp, self->items,len*sizeof(tp_obj)); 10 | self->alloc = len; 11 | } 12 | 13 | void tpd_list_set(TP, tpd_list *self,int k, tp_obj v, const char *error) { 14 | if (k >= self->len) { 15 | tp_raise(,tp_string_atom(tp, "(tpd_list_set) KeyError")); 16 | } 17 | self->items[k] = v; 18 | tp_grey(tp, v); 19 | } 20 | 21 | tpd_list *tpd_list_new(TP) { 22 | return (tpd_list*) tp_malloc(tp, sizeof(tpd_list)); 23 | } 24 | 25 | void tpd_list_free(TP, tpd_list *self) { 26 | tp_free(tp, self->items); 27 | tp_free(tp, self); 28 | } 29 | 30 | tp_obj tpd_list_get(TP, tpd_list *self, int k, const char *error) { 31 | if (k >= self->len) { 32 | tp_raise_printf(tp_None, "(tpd_list_get) KeyError : Index %d request, but length is %d", k, self->len); 33 | } 34 | return self->items[k]; 35 | } 36 | void tpd_list_insertx(TP, tpd_list *self, int n, tp_obj v) { 37 | if (self->len >= self->alloc) { 38 | tpd_list_realloc(tp, self,self->alloc*2); 39 | } 40 | if (n < self->len) { memmove(&self->items[n+1],&self->items[n],sizeof(tp_obj)*(self->len-n)); } 41 | self->items[n] = v; 42 | self->len += 1; 43 | } 44 | void tpd_list_appendx(TP, tpd_list *self, tp_obj v) { 45 | tpd_list_insertx(tp, self, self->len, v); 46 | } 47 | void tpd_list_insert(TP,tpd_list *self, int n, tp_obj v) { 48 | tpd_list_insertx(tp,self,n,v); 49 | tp_grey(tp, v); 50 | } 51 | void tpd_list_append(TP,tpd_list *self, tp_obj v) { 52 | tpd_list_insert(tp,self,self->len,v); 53 | } 54 | 55 | void tpd_list_extend(TP, tpd_list * self, tpd_list * v) { 56 | int i; 57 | for (i = 0; i < v->len; i++) { 58 | tpd_list_append(tp, self, v->items[i]); 59 | } 60 | } 61 | 62 | tp_obj tpd_list_pop(TP,tpd_list *self, int n, const char *error) { 63 | tp_obj r = tpd_list_get(tp,self,n,error); 64 | if (n != self->len-1) { 65 | memmove(&self->items[n], &self->items[n+1], sizeof(tp_obj)*(self->len-(n+1))); 66 | } 67 | self->len -= 1; 68 | return r; 69 | } 70 | 71 | int tpd_list_find(TP, tpd_list * self, tp_obj v, int (*equal)(TP, tp_obj self, tp_obj v)) { 72 | int n; 73 | for (n=0; nlen; n++) { 74 | if (equal(tp, v, self->items[n])) { 75 | return n; 76 | } 77 | } 78 | return -1; 79 | } 80 | -------------------------------------------------------------------------------- /tests/test_str.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from tinypy.runtime.testing import UnitTest 3 | 4 | class StringUnitTests(UnitTest): 5 | 6 | def test_eq(self): 7 | assert "a" == "a" 8 | assert "abcdef" == "abcdef" 9 | assert "abcdef\0" != "abcdef" 10 | 11 | def test_replace(self): 12 | assert "".replace("a", "b") == "" 13 | assert "c".replace("a", "b") == "c" 14 | assert "a".replace("a", "b") == "b" 15 | assert "aa".replace("a", "b") == "bb" 16 | assert "ab".replace("ab", "b") == "b" 17 | assert "aab".replace("ab", "b") == "ab" 18 | assert "3ab".replace("ab", "b") == "3b" 19 | assert "3ab1".replace("ab", "b") == "3b1" 20 | 21 | def test_startswith(self): 22 | a = "012345" 23 | assert a.startswith("0") 24 | assert a.startswith("01") 25 | assert not a.startswith("1") 26 | 27 | def test_format(self): 28 | a = "{foo}{bar}d".format(dict(foo='abc', bar='123')) 29 | assert a == "abc123d" 30 | 31 | def test_percent(self): 32 | a = "{foo}d" % dict(foo='abc') 33 | assert a == "abcd" 34 | 35 | def test_find(self): 36 | a = "012345" 37 | assert a.find("0") == 0 38 | assert a.find("1") == 1 39 | assert a.find("9") == -1 40 | 41 | def test_index(self): 42 | a = "012345" 43 | assert a.index("0") == 0 44 | try: 45 | assert a.index("9") == -1 46 | except: 47 | assert "ValueError" in sys.get_exc()[0] 48 | 49 | def test_split(self): 50 | a = "012345" 51 | s = a.split('3') 52 | assert len(s) == 2 53 | assert s[0] == '012' 54 | assert s[1] == '45' 55 | 56 | def test_join(self): 57 | j = ' '.join(['abc', 'def']) 58 | assert j == 'abc def' 59 | 60 | def test_slice(self): 61 | assert '0123'[1, 2] == '1' 62 | assert '0123'[1, None] == '123' 63 | assert '0123'[None, None] == '0123' 64 | assert '0123'[None, 1] == '0' 65 | assert '0123'[None, 2] == '01' 66 | 67 | def test_chr(self): 68 | j = chr(0) + chr(65) + chr(66) + chr(0) 69 | assert j == '\0AB\0' 70 | assert len(j) == 4 71 | 72 | def test_escape(self): 73 | assert ord("\\") == 92 74 | assert ord("\n") == 10 75 | assert ord("\r") == 13 76 | assert ord("\t") == 9 77 | assert ord("\0") == 0 78 | assert len("\xff") == 1 79 | assert ord("\xff") == 255 80 | assert ord("\x0f") == 15 81 | 82 | if __name__ == '__main__': 83 | tests = StringUnitTests() 84 | tests.run() 85 | 86 | -------------------------------------------------------------------------------- /README.txt.old: -------------------------------------------------------------------------------- 1 | 2 | ==== Redirected ==== 3 | 4 | A more recent development of tinypy is in the form of tinypy++ (C++ based) 5 | at https://gitlab.com/hartsantler/tpythonpp. 6 | 7 | ==== End of Redirection ==== 8 | 9 | 64k tinypy 10 | "batteries not (yet) included" 11 | Copyright (c) 2008 Phil Hassey 12 | 13 | This version adds: 14 | - dl module 15 | - jni module + java interface 16 | 17 | Check it out: 18 | 19 | $ python setup.py linux pygame 20 | $ ./build/tinypy examples/julia.py 21 | $ ./build/tinypy your-program-goes-here.py 22 | 23 | Depends on: 24 | - python (only for bootstrapping) 25 | - sdl (for the pygame module) 26 | - gcc 27 | 28 | Credits: 29 | - math module - Rockins Chen 30 | - VS support - Krzysztof Kowalczyk 31 | - bug fixin' - Dean Hall & Allefant 32 | 33 | Thanks to allefant and the python community for all the tips and feedback! 34 | Thanks to John M. for a python 2.5 compat. patch. 35 | And to illume and the rest of #ludumdare for morale support. 36 | Also thanks to python.org, lua.org, valgrind.org, nekovm.org, pypy.org 37 | http://javascript.crockford.com/tdop/tdop.html 38 | http://www.memorymanagement.org/articles/recycle.html 39 | http://shed-skin.blogspot.com/ 40 | 41 | Other "tiny" python implementations: 42 | http://pymite.python-hosting.com/ 43 | http://students.ceid.upatras.gr/~sxanth/pyvm/ 44 | 45 | F.A.Q.s: 46 | 47 | Q. If I run boot.py it says you've got like 80k of code! That's TOTALLY 48 | not 64k! I want my money back. 49 | A. Err... that's true. But 64k sounds *SO* much better than 80k. 50 | If you *really* want it to be 64k, just run: 51 | $ python mk64k.py 52 | This does the following things: 53 | - changes 4 spaces into tabs and removes blank lines 54 | - removes comments 55 | - removes the "namespacing" i.e. "tp_print" becomes "print" 56 | 57 | Q. The binary is bigger than 64k. I hate big binaries. 58 | A. I don't really care, but if you run "upx tinypy" it makes the tinypy 59 | binary smaller than 64k. 60 | 61 | Q. No matter how you spin this, it's just plain NOT 64k. 62 | A. Let the buyer beware? I dunno, it's close enough. Let's call it a rounding 63 | error, shall we? 64 | 65 | Q. How come some oddball combinations of variable and named arguments don't work? 66 | A. Ask me some other time. Short answer: I do it like lua does it. Only calls 67 | like this make sense: 68 | call_with_var_args(a,b,c,*d) 69 | call_with_named_args(a=b,**c) 70 | mixes of both just don't work, sorry! 71 | 72 | Q. At the end of build.py tinypy doesn't work! 73 | A. This is probably because of my use of -O3 in the final step. Run the command 74 | again without -O3. Some versions of GCC are buggy and don't do well with it. 75 | -------------------------------------------------------------------------------- /modules/random/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "random.c" 4 | 5 | /* 6 | * random_mod_init() 7 | * 8 | * random module initialization function 9 | */ 10 | void random_init(TP) 11 | { 12 | /* 13 | * module dict for random 14 | */ 15 | tp_obj random_mod = tp_dict_t(tp); 16 | 17 | /* 18 | * bind functions to random module 19 | */ 20 | tp_set(tp, random_mod, tp_string_atom(tp, "seed"), tp_function(tp, random_seed)); 21 | tp_set(tp, random_mod, tp_string_atom(tp, "getstate"), tp_function(tp, random_getstate)); 22 | tp_set(tp, random_mod, tp_string_atom(tp, "setstate"), tp_function(tp, random_setstate)); 23 | tp_set(tp, random_mod, tp_string_atom(tp, "jumpahead"), tp_function(tp, random_jumpahead)); 24 | tp_set(tp, random_mod, tp_string_atom(tp, "random"), tp_function(tp, random_random)); 25 | 26 | /* 27 | * bind usual distribution random variable generator 28 | */ 29 | tp_set(tp, random_mod, tp_string_atom(tp, "uniform"), tp_function(tp, random_uniform)); 30 | tp_set(tp, random_mod, tp_string_atom(tp, "normalvariate"), tp_function(tp, random_normalvariate)); 31 | tp_set(tp, random_mod, tp_string_atom(tp, "lognormvariate"), tp_function(tp, random_lognormvariate)); 32 | tp_set(tp, random_mod, tp_string_atom(tp, "expovariate"), tp_function(tp, random_expovariate)); 33 | tp_set(tp, random_mod, tp_string_atom(tp, "vonmisesvariate"), tp_function(tp, random_vonmisesvariate)); 34 | tp_set(tp, random_mod, tp_string_atom(tp, "gammavariate"), tp_function(tp, random_gammavariate)); 35 | tp_set(tp, random_mod, tp_string_atom(tp, "betavariate"), tp_function(tp, random_betavariate)); 36 | tp_set(tp, random_mod, tp_string_atom(tp, "paretovariate"), tp_function(tp, random_paretovariate)); 37 | tp_set(tp, random_mod, tp_string_atom(tp, "weibullvariate"), tp_function(tp, random_weibullvariate)); 38 | tp_set(tp, random_mod, tp_string_atom(tp, "randrange"), tp_function(tp, random_randrange)); 39 | tp_set(tp, random_mod, tp_string_atom(tp, "randint"), tp_function(tp, random_randint)); 40 | tp_set(tp, random_mod, tp_string_atom(tp, "choice"), tp_function(tp, random_choice)); 41 | tp_set(tp, random_mod, tp_string_atom(tp, "shuffle"), tp_function(tp, random_shuffle)); 42 | 43 | /* 44 | * bind special attributes to random module 45 | */ 46 | tp_set(tp, random_mod, tp_string_atom(tp, "__doc__"), tp_string_atom(tp, "Random variable generators.")); 47 | tp_set(tp, random_mod, tp_string_atom(tp, "__name__"), tp_string_atom(tp, "random")); 48 | tp_set(tp, random_mod, tp_string_atom(tp, "__file__"), tp_string_atom(tp, __FILE__)); 49 | 50 | /* 51 | * bind random module to tinypy modules[] 52 | */ 53 | tp_set(tp, tp->modules, tp_string_atom(tp, "random"), random_mod); 54 | } 55 | -------------------------------------------------------------------------------- /tinypy/tp_frame.c: -------------------------------------------------------------------------------- 1 | tp_obj tp_frame_t(TP, tp_obj lparams, tp_obj dparams, 2 | tp_obj globals, tp_obj code, 3 | tp_obj args, tp_obj defaults, tp_obj * ret_dest) { 4 | tp_obj r = {TP_FRAME}; 5 | r.info = tp_malloc(tp, sizeof(tpd_frame)); 6 | tpd_frame * f = TPD_FRAME(r); 7 | 8 | f->globals = globals; 9 | f->code = code; 10 | f->cur = (tpd_code*) tp_string_getptr(f->code); 11 | f->jmp = 0; 12 | f->ret_dest = ret_dest; 13 | f->lineno = 0; 14 | f->lparams = lparams; 15 | f->dparams = dparams; 16 | f->args = args; 17 | f->defaults = defaults; 18 | f->line = tp->chars['?']; 19 | f->name = tp->chars['?']; 20 | f->fname = tp->chars['?']; 21 | f->cregs = 0; 22 | return tp_track(tp, r); 23 | } 24 | 25 | void tpd_frame_alloc(TP, tpd_frame * f, tp_obj * regs, int cregs) { 26 | /* call convention requires 1 reg for __params__.*/ 27 | f->regs = regs; 28 | 29 | int nargs = tp_none(f->args)?0:TPD_LIST(f->args)->len; 30 | tp_obj * args = tp_none(f->args)?NULL:TPD_LIST(f->args)->items; 31 | 32 | int ndefaults = tp_none(f->defaults)?0:TPD_LIST(f->defaults)->len; 33 | tp_obj * defaults = tp_none(f->defaults)?NULL:TPD_LIST(f->defaults)->items; 34 | int i; 35 | int nrequired = nargs - ndefaults; 36 | int nlparams = tp_none(f->lparams)?0:TPD_LIST(f->lparams)->len; 37 | 38 | tp_obj varkw = tp_none(f->dparams)?tp_dict_t(tp):tp_dict_copy(tp, f->dparams); 39 | 40 | for(i = 0; i < nargs; i ++) { 41 | /* calling convention local 0...len(args)-1 = args. local len(args) = varargs. local len(args)+1 = varkw */ 42 | if(i < nlparams) { 43 | if(tp_dict_has(tp, varkw, args[i])) { 44 | tp_raise_printf(, "(tp_call): argument %O given both as positional and keyword", &args[i]); 45 | } 46 | f->regs[i] = TPD_LIST(f->lparams)->items[i]; 47 | } else { 48 | if(tp_dict_has(tp, varkw, args[i])) { 49 | f->regs[i] = tp_dict_get(tp, varkw, args[i]); 50 | tp_dict_del(tp, varkw, args[i]); 51 | } else if(i >= nrequired) { 52 | f->regs[i] = defaults[i - nrequired]; 53 | } else { 54 | tp_raise_printf(, "(tp_call): argument %O is missing", &args[i]); 55 | } 56 | } 57 | } 58 | if (nargs < nlparams) { 59 | f->regs[nargs] = tp_list_from_items(tp, nlparams - nargs, &TPD_LIST(f->lparams)->items[nargs]); 60 | } else { 61 | f->regs[nargs] = tp_None; 62 | } 63 | 64 | f->regs[nargs + 1] = varkw; 65 | 66 | if(cregs < nargs + 1) { 67 | abort(); 68 | } 69 | #if 0 70 | printf("nargs = %d f->args = %s\n", nargs, tp_cstr(tp, tp_str(tp, f->args))); 71 | for(i = 0; i < cregs; i ++) { 72 | printf("regs[%d] %s\n", i, tp_cstr(tp, tp_str(tp, f->regs[i]))); 73 | } 74 | #endif 75 | f->cregs = cregs; 76 | } 77 | -------------------------------------------------------------------------------- /tinypy/tp_meta.c: -------------------------------------------------------------------------------- 1 | void tp_set_meta(TP, tp_obj self, tp_obj meta) { 2 | if(self.type.typeid != TP_DICT) { 3 | tp_raise(, 4 | tp_string_atom(tp, "(tp_check_type) TypeError: type does not support meta.")); 5 | } 6 | TPD_DICT(self)->meta = meta; 7 | } 8 | tp_obj tp_get_meta(TP, tp_obj self) { 9 | if(self.type.typeid == TP_STRING) { 10 | return tp->string_class; 11 | } 12 | if(self.type.typeid == TP_LIST) { 13 | return tp->list_class; 14 | } 15 | if(self.type.typeid == TP_DICT && self.type.magic == TP_DICT_RAW) { 16 | return tp->dict_class; 17 | } 18 | if(self.type.typeid == TP_DICT && self.type.magic != TP_DICT_RAW) { 19 | return TPD_DICT(self)->meta; 20 | } 21 | return tp_None; 22 | } 23 | 24 | int _tp_lookup_(TP, tp_obj self, int hash, tp_obj k, tp_obj *r, int depth) { 25 | /* first do a dict look up from the object itself, but never look for values from a raw dict. */ 26 | if(self.type.typeid == TP_DICT && self.type.magic != TP_DICT_RAW) { 27 | int n = tpd_dict_hashfind(tp, TPD_DICT(self), hash, k); 28 | if (n != -1) { 29 | *r= TPD_DICT(self)->items[n].val; 30 | return 1; 31 | } 32 | /* raw dict, no meta chain up. we are done. */ 33 | if(self.type.magic == TP_DICT_RAW) 34 | return 0; 35 | } 36 | 37 | depth--; 38 | if (!depth) { 39 | tp_raise(0,tp_string_atom(tp, "(tp_lookup) RuntimeError: maximum lookup depth exceeded")); 40 | } 41 | 42 | tp_obj meta = tp_get_meta(tp, self); 43 | if (tp_none(meta)) { 44 | return 0; 45 | } 46 | 47 | if (meta.type.typeid == TP_DICT && 48 | _tp_lookup_(tp, meta, hash, k, r, depth)) { 49 | if ( r->type.typeid == TP_FUNC && 0 == (r->type.mask & TP_FUNC_MASK_STATIC)) { 50 | /* object dict or string, or list */ 51 | if ((self.type.typeid == TP_DICT && self.type.magic != TP_DICT_CLASS) 52 | || self.type.typeid == TP_LIST 53 | || self.type.typeid == TP_STRING 54 | ) { 55 | *r = tp_bind(tp, *r, self); 56 | } 57 | } 58 | return 1; 59 | } 60 | return 0; 61 | } 62 | 63 | int _tp_lookup(TP, tp_obj self, tp_obj k, tp_obj *r) { 64 | return _tp_lookup_(tp, self, tp_hash(tp, k), k, r, 8); 65 | } 66 | 67 | #define TP_META_BEGIN(self, name) \ 68 | if ((self.type.typeid == TP_DICT && self.type.magic == TP_DICT_OBJECT) || \ 69 | (self.type.typeid == TP_STRING || self.type.typeid == TP_LIST ) \ 70 | ) { \ 71 | tp_obj name; if (_tp_lookup(tp, self, tp_string_atom(tp, #name), &name)) { 72 | #define TP_META_END \ 73 | } \ 74 | } 75 | 76 | #define TP_META_BEGIN_CLASS(self, name) \ 77 | if ((self.type.typeid == TP_DICT && self.type.magic == TP_DICT_CLASS) \ 78 | ) { \ 79 | tp_obj name; if (_tp_lookup(tp, self, tp_string_atom(tp, #name), &name)) { 80 | #define TP_META_END_CLASS \ 81 | } \ 82 | } 83 | 84 | -------------------------------------------------------------------------------- /tinypy/interp/sandbox.c: -------------------------------------------------------------------------------- 1 | 2 | void tp_sandbox(TP, double time_limit, unsigned long mem_limit) { 3 | tp->time_limit = time_limit; 4 | tp->mem_limit = mem_limit; 5 | } 6 | 7 | void tp_mem_update(TP) { 8 | /* static long maxmem = 0; 9 | if (tp->mem_used/1024 > maxmem) { 10 | maxmem = tp->mem_used/1024; 11 | fprintf(stderr,"%ld k\n",maxmem); 12 | }*/ 13 | if((!tp->mem_exceeded) && 14 | (tp->mem_used > tp->mem_limit) && 15 | (tp->mem_limit != TP_NO_LIMIT)) { 16 | tp->mem_exceeded = 1; 17 | tp_raise(,tp_string_atom(tp, "(tp_mem_update) SandboxError: memory limit exceeded")); 18 | } 19 | } 20 | 21 | void tp_time_update(TP) { 22 | clock_t tmp = tp->clocks; 23 | if(tp->time_limit != TP_NO_LIMIT) 24 | { 25 | tp->clocks = clock(); 26 | tp->time_elapsed += ((double) (tp->clocks - tmp) / CLOCKS_PER_SEC) * 1000.0; 27 | if(tp->time_elapsed >= tp->time_limit) 28 | tp_raise(,tp_string_atom(tp, "(tp_time_update) SandboxError: time limit exceeded")); 29 | } 30 | } 31 | 32 | #ifdef TP_SANDBOX 33 | 34 | void *tp_malloc(TP, unsigned long bytes) { 35 | unsigned long *ptr = (unsigned long *) calloc(bytes + sizeof(unsigned long), 1); 36 | if(ptr) { 37 | *ptr = bytes; 38 | tp->mem_used += bytes + sizeof(unsigned long); 39 | } 40 | tp_mem_update(tp); 41 | return ptr+1; 42 | } 43 | 44 | void tp_free(TP, void *ptr) { 45 | unsigned long *temp = (unsigned long *) ptr; 46 | if(temp) { 47 | --temp; 48 | tp->mem_used -= (*temp + sizeof(unsigned long)); 49 | free(temp); 50 | } 51 | tp_mem_update(tp); 52 | } 53 | 54 | void *tp_realloc(TP, void *ptr, unsigned long bytes) { 55 | unsigned long *temp = (unsigned long *) ptr; 56 | int diff; 57 | if(temp && bytes) { 58 | --temp; 59 | diff = bytes - *temp; 60 | *temp = bytes; 61 | tp->mem_used += diff; 62 | temp = (unsigned long *) realloc(temp, bytes+sizeof(unsigned long)); 63 | return temp+1; 64 | } 65 | else if(temp && !bytes) { 66 | tp_free(tp, temp); 67 | return NULL; 68 | } 69 | else if(!temp && bytes) { 70 | return tp_malloc(tp, bytes); 71 | } 72 | else { 73 | return NULL; 74 | } 75 | } 76 | 77 | #endif 78 | 79 | tp_obj tp_sandbox_(TP) { 80 | tp_num time = TP_PARAMS_FLOAT(); 81 | tp_num mem = TP_PARAMS_FLOAT(); 82 | tp_sandbox(tp, time, mem); 83 | tp_del(tp, tp->builtins, tp_string_atom(tp, "sandbox")); 84 | tp_del(tp, tp->builtins, tp_string_atom(tp, "mtime")); 85 | tp_del(tp, tp->builtins, tp_string_atom(tp, "load")); 86 | tp_del(tp, tp->builtins, tp_string_atom(tp, "save")); 87 | tp_del(tp, tp->builtins, tp_string_atom(tp, "system")); 88 | return tp_None; 89 | } 90 | 91 | void tp_bounds(TP, tpd_code *cur, int n) { 92 | char *s = (char *)(cur + n); 93 | tp_obj code = tp->frames[tp->cur].code; 94 | if (s < code.string.info->s || s > (code.string.info->s+tp_string_len(code))) { 95 | tp_raise(,tp_string_atom(tp, "(tp_bounds) SandboxError: bytecode bounds reached")); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_func.py: -------------------------------------------------------------------------------- 1 | from tinypy.runtime.testing import UnitTest 2 | 3 | class FuncTest(UnitTest): 4 | 5 | def test_attrs(self): 6 | def func(a, b, c=3, *args, **kwargs): 7 | pass 8 | assert func.__args__ == ('a', 'b', 'c') 9 | assert func.__defaults__ == [3] 10 | assert func.__varargs__ == 'args' 11 | assert func.__varkw__ == 'kwargs' 12 | 13 | def test_pos(self): 14 | def func(a, b): 15 | return a, b 16 | assert func(1, 2) == (1, 2) 17 | 18 | def test_pos_args(self): 19 | def func(a, b, *c): 20 | return a, b, c 21 | assert func(1, 2, 3, 4) == (1, 2, (3, 4)) 22 | 23 | def test_default_pos(self): 24 | def func(a, b=9, c=9): 25 | return a, b, c 26 | assert func(1) == (1, 9, 9) 27 | assert func(1, 2) == (1, 2, 9) 28 | assert func(1, 2, 3) == (1, 2, 3) 29 | 30 | def test_default_pos_mixed(self): 31 | def func(a, b=9, c=9): 32 | return a, b, c 33 | assert func(1, b=9) == (1, 9, 9) 34 | 35 | def test_kwargs(self): 36 | def func(**c): 37 | return c 38 | c = func(c1=1, c2=2) 39 | assert c['c1'] == 1 40 | assert c['c2'] == 2 41 | 42 | def test_args(self): 43 | def func(*args): 44 | return args 45 | c = func(1, 2) 46 | assert c == [1, 2] 47 | 48 | def test_mix_kw_default(self): 49 | def func(a, b=9, c=9): 50 | return a, b, c 51 | assert func(1, 2, c=3) == (1, 2, 3) 52 | assert func(1, b=2, c=3) == (1, 2, 3) 53 | assert func(a=1, b=2, c=3) == (1, 2, 3) 54 | 55 | def test_args_and_kwargs(self): 56 | def func(*args, **kwargs): 57 | return args, kwargs 58 | c, d = func(1, 2, d=1) 59 | assert c == [1, 2] 60 | assert d == {'d': 1} 61 | 62 | def test_pos_kwargs(self): 63 | def func(a, **kwargs): 64 | return a, kwargs 65 | a, d = func(1, d1=2, d2=3) 66 | assert a == 1 67 | assert d['d1'] == 2 68 | assert d['d2'] == 3 69 | 70 | def known_failure_test_lparams_overwrites_dparams(self): 71 | # We are following CPython now. 72 | def func(a, b): 73 | return a, b 74 | # unlikely CPython, we use positional argument 75 | # if dparams conflicts with positional argument. 76 | a, b = func(1, **{'a':2, 'b':3}) 77 | assert a == 1 78 | assert b == 3 79 | 80 | def test_dparams_overwrites_default(self): 81 | def func(a, b=1): 82 | return a, b 83 | # dparams overwrites default value. 84 | a, b = func(**dict(a=2, b=3)) 85 | assert a == 2 86 | assert b == 3 87 | 88 | def test_lparams_overwrites_default(self): 89 | def func(a, b=2): 90 | return a, b 91 | # lparams overwrites default value. 92 | a, b = func(*[2, 3]) 93 | assert a == 2 94 | assert b == 3 95 | 96 | 97 | if __name__ == '__main__': 98 | t = FuncTest() 99 | t.run() 100 | -------------------------------------------------------------------------------- /tinypy/runtime/testing.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from tinypy.runtime.types import Exception 3 | 4 | def fgcolor(code): 5 | ESC = chr(27) 6 | if code: 7 | return ESC + "[1;" + str(code) + "m" 8 | else: 9 | return ESC + "[0m" 10 | 11 | class TestResult: 12 | def __init__(self, name, passed, exc, stack): 13 | self.passed = passed 14 | self.name = name 15 | self.exc = exc 16 | self.stack = stack 17 | 18 | class TestError(Exception): 19 | pass 20 | 21 | class UnitTest: 22 | def __init__(self): 23 | self._known_failure_prefix = 'known_failure_test' 24 | self._test_prefix = 'test_' 25 | 26 | def discover(self, test_prefix, known_failure_prefix): 27 | r = [] 28 | meta = getmeta(self) 29 | for name in meta: 30 | if name.find(known_failure_prefix) == 0: 31 | r.append(name) 32 | if name.find(test_prefix) == 0: 33 | r.append(name) 34 | return r 35 | 36 | def setup(self, testname): 37 | pass 38 | 39 | def teardown(self, testname): 40 | pass 41 | 42 | def run(self, monitor=print): 43 | tests = self.discover(self._test_prefix, self._known_failure_prefix) 44 | tests.sort() 45 | 46 | subst = dict(script=sys.argv[0], total=len(tests)) 47 | 48 | subst['RED'] = fgcolor(31) 49 | subst['GREEN'] = fgcolor(32) 50 | subst['RESET'] = fgcolor(0) 51 | 52 | monitor("{GREEN}[ STARTED ]{RESET} {script} {total} cases.".format(subst)) 53 | 54 | nfail = 0 55 | nknownfail = 0 56 | itest = 0 57 | for test in tests: 58 | testfunc = self[test] 59 | result = self.runone(test, testfunc) 60 | subst['name'] = test 61 | if result.passed: 62 | subst['status'] = "PASS" 63 | elif test.startswith(self._known_failure_prefix): 64 | nknownfail = nknownfail + 1 65 | subst['status'] = "KNOWN" 66 | else: 67 | nfail = nfail + 1 68 | subst['status'] = "FAIL" 69 | subst['id'] = itest 70 | msg = "[ {status} ] {id}: {name}".format(subst) 71 | itest = itest + 1 72 | monitor(msg) 73 | if not result.passed: 74 | monitor("Exception:\n{exc}\n{stack}".format(result)) 75 | 76 | subst['nfail'] = nfail 77 | subst['nknownfail'] = nknownfail 78 | if nfail > 0: 79 | msg = "{RED}[ FAIL ]{RESET} {script} {nfail} Failed.".format(subst) 80 | else: 81 | msg = "{GREEN}[ HEALTHY ]{RESET} {script} {nknownfail} Known Failures.".format(subst) 82 | 83 | monitor(msg) 84 | if nfail > 0: 85 | sys.exit(255) 86 | return False 87 | sys.exit(0) 88 | return True 89 | 90 | def runone(self, test, testfunc): 91 | self.setup(test) 92 | try: 93 | testfunc(self) 94 | return TestResult(test, True, None, None) 95 | except: 96 | exc, stack = sys.get_exc() 97 | return TestResult(test, False, exc, stack) 98 | self.teardown(test) 99 | -------------------------------------------------------------------------------- /tinypy/tpd_dict.c: -------------------------------------------------------------------------------- 1 | void tpd_dict_free(TP, tpd_dict *self) { 2 | tp_free(tp, self->items); 3 | tp_free(tp, self); 4 | } 5 | 6 | /* void tpd_dict_reset(tpd_dict *self) { 7 | memset(self->items,0,self->alloc*sizeof(tpd_item)); 8 | self->len = 0; 9 | self->used = 0; 10 | self->cur = 0; 11 | }*/ 12 | 13 | void tpd_dict_hashset(TP, tpd_dict *self, int hash, tp_obj k, tp_obj v) { 14 | tpd_item item; 15 | int i,idx = hash&self->mask; 16 | for (i=idx; ialloc; i++) { 17 | int n = i&self->mask; 18 | if (self->items[n].used > 0) { continue; } 19 | if (self->items[n].used == 0) { self->used += 1; } 20 | item.used = 1; 21 | item.hash = hash; 22 | item.key = k; 23 | item.val = v; 24 | self->items[n] = item; 25 | self->len += 1; 26 | return; 27 | } 28 | } 29 | 30 | void tpd_dict_realloc(TP, tpd_dict *self, int len) { 31 | tpd_item *items = self->items; 32 | int i,alloc = self->alloc; 33 | len = _tp_max(8,len); 34 | 35 | self->items = (tpd_item*)tp_malloc(tp, len*sizeof(tpd_item)); 36 | self->alloc = len; self->mask = len-1; 37 | self->len = 0; self->used = 0; 38 | 39 | for (i=0; imask; 48 | for (i=idx; ialloc; i++) { 49 | int n = i&self->mask; 50 | if (self->items[n].used == 0) { break; } 51 | if (self->items[n].used < 0) { continue; } 52 | if (self->items[n].hash != hash) { continue; } 53 | if (!tp_equal(tp, self->items[n].key, k)) { continue; } 54 | return n; 55 | } 56 | return -1; 57 | } 58 | 59 | tpd_dict *tpd_dict_new(TP) { 60 | tpd_dict *self = (tpd_dict*) tp_malloc(tp, sizeof(tpd_dict)); 61 | return self; 62 | } 63 | 64 | void tpd_dict_hashsetx(TP, tpd_dict * self, int hash, tp_obj k, tp_obj v) { 65 | int n = tpd_dict_hashfind(tp, self, hash, k); 66 | if (n == -1) { 67 | if (self->len >= (self->alloc/2)) { 68 | tpd_dict_realloc(tp, self, self->alloc*2); 69 | } else 70 | if (self->used >= (self->alloc /4 * 3)) { 71 | /* compact the dict */ 72 | tpd_dict_realloc(tp, self, self->alloc); 73 | } 74 | tpd_dict_hashset(tp, self, hash, k, v); 75 | } else { 76 | self->items[n].val = v; 77 | } 78 | } 79 | 80 | int tpd_dict_next(TP, tpd_dict *self) { 81 | if (!self->len) { 82 | return -1; 83 | } 84 | while (1) { 85 | self->cur = ((self->cur + 1) & self->mask); 86 | if (self->items[self->cur].used > 0) { 87 | return self->cur; 88 | } 89 | } 90 | } 91 | 92 | tp_obj tpd_dict_get(TP, tpd_dict * self, int n) { 93 | return self->items[n].val; 94 | } 95 | 96 | void tpd_dict_del(TP, tpd_dict * self, int n) { 97 | self->items[n].used = -1; 98 | self->len -= 1; 99 | } 100 | -------------------------------------------------------------------------------- /tinypy/printf/mini-printf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The Minimal snprintf() implementation 3 | * 4 | * Copyright (c) 2013 Michal Ludvig 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of the auhor nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #ifndef __MINI_PRINTF__ 32 | #define __MINI_PRINTF__ 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | #include 39 | 40 | #ifdef MINI_PRINTF_ENABLE_OBJECTS 41 | /* If enabled, callback for object types (O and R). 42 | * void* arguments matching %O and %R are sent to handler as obj. 43 | * the result string created by handler at *buf is freed by freeor. 44 | * */ 45 | void mini_printf_set_handler( 46 | void * data, 47 | /* handler returns number of chars in *buf; *buf is not NUL-terminated. */ 48 | int (*handler)(void* data, void* obj, int ch, int len_hint, char** buf), 49 | void (*freeor)(void* data, void* buf)); 50 | #endif 51 | 52 | /* String IO interface; returns number of bytes written, not including the ending NUL. 53 | * Always appends a NUL at the end, therefore buffer_len shall be at least 1 in normal operation. 54 | * If buffer is NULL or buffer_len is 0, returns number of bytes to be written, not including the ending NUL. 55 | */ 56 | int mini_vsnprintf(char* buffer, unsigned int buffer_len, const char *fmt, va_list va); 57 | int mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...); 58 | 59 | /* Stream IO interface; returns number of bytes written. 60 | * If puts is NULL, number of bytes to be written. 61 | * puts shall return number of bytes written. 62 | */ 63 | int mini_vpprintf(int (*puts)(char* s, unsigned int len, void* buf), void* buf, const char *fmt, va_list va); 64 | int mini_pprintf(int (*puts)(char*s, unsigned int len, void* buf), void* buf, const char *fmt, ...); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /modules/math/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "math.c" 3 | 4 | /* 5 | * init math module, namely, set its dictionary 6 | */ 7 | void math_init(TP) 8 | { 9 | /* 10 | * new a module dict for math 11 | */ 12 | tp_obj math_mod = tp_dict_t(tp); 13 | 14 | /* 15 | * initialize pi and e 16 | */ 17 | math_pi = tp_float(M_PI); 18 | math_e = tp_float(M_E); 19 | 20 | /* 21 | * bind math functions to math module 22 | */ 23 | tp_set(tp, math_mod, tp_string_atom(tp, "pi"), math_pi); 24 | tp_set(tp, math_mod, tp_string_atom(tp, "e"), math_e); 25 | tp_set(tp, math_mod, tp_string_atom(tp, "acos"), tp_function(tp, math_acos)); 26 | tp_set(tp, math_mod, tp_string_atom(tp, "asin"), tp_function(tp, math_asin)); 27 | tp_set(tp, math_mod, tp_string_atom(tp, "atan"), tp_function(tp, math_atan)); 28 | tp_set(tp, math_mod, tp_string_atom(tp, "atan2"), tp_function(tp, math_atan2)); 29 | tp_set(tp, math_mod, tp_string_atom(tp, "ceil"), tp_function(tp, math_ceil)); 30 | tp_set(tp, math_mod, tp_string_atom(tp, "cos"), tp_function(tp, math_cos)); 31 | tp_set(tp, math_mod, tp_string_atom(tp, "cosh"), tp_function(tp, math_cosh)); 32 | tp_set(tp, math_mod, tp_string_atom(tp, "degrees"), tp_function(tp, math_degrees)); 33 | tp_set(tp, math_mod, tp_string_atom(tp, "exp"), tp_function(tp, math_exp)); 34 | tp_set(tp, math_mod, tp_string_atom(tp, "fabs"), tp_function(tp, math_fabs)); 35 | tp_set(tp, math_mod, tp_string_atom(tp, "floor"), tp_function(tp, math_floor)); 36 | tp_set(tp, math_mod, tp_string_atom(tp, "fmod"), tp_function(tp, math_fmod)); 37 | tp_set(tp, math_mod, tp_string_atom(tp, "frexp"), tp_function(tp, math_frexp)); 38 | tp_set(tp, math_mod, tp_string_atom(tp, "hypot"), tp_function(tp, math_hypot)); 39 | tp_set(tp, math_mod, tp_string_atom(tp, "ldexp"), tp_function(tp, math_ldexp)); 40 | tp_set(tp, math_mod, tp_string_atom(tp, "log"), tp_function(tp, math_log)); 41 | tp_set(tp, math_mod, tp_string_atom(tp, "log10"), tp_function(tp, math_log10)); 42 | tp_set(tp, math_mod, tp_string_atom(tp, "modf"), tp_function(tp, math_modf)); 43 | tp_set(tp, math_mod, tp_string_atom(tp, "pow"), tp_function(tp, math_pow)); 44 | tp_set(tp, math_mod, tp_string_atom(tp, "radians"), tp_function(tp, math_radians)); 45 | tp_set(tp, math_mod, tp_string_atom(tp, "sin"), tp_function(tp, math_sin)); 46 | tp_set(tp, math_mod, tp_string_atom(tp, "sinh"), tp_function(tp, math_sinh)); 47 | tp_set(tp, math_mod, tp_string_atom(tp, "sqrt"), tp_function(tp, math_sqrt)); 48 | tp_set(tp, math_mod, tp_string_atom(tp, "tan"), tp_function(tp, math_tan)); 49 | tp_set(tp, math_mod, tp_string_atom(tp, "tanh"), tp_function(tp, math_tanh)); 50 | 51 | /* 52 | * bind special attributes to math module 53 | */ 54 | tp_set(tp, math_mod, tp_string_atom(tp, "__doc__"), 55 | tp_string_atom(tp, 56 | "This module is always available. It provides access to the\n" 57 | "mathematical functions defined by the C standard.")); 58 | tp_set(tp, math_mod, tp_string_atom(tp, "__name__"), tp_string_atom(tp, "math")); 59 | tp_set(tp, math_mod, tp_string_atom(tp, "__file__"), tp_string_atom(tp, __FILE__)); 60 | 61 | /* 62 | * bind to tiny modules[] 63 | */ 64 | tp_set(tp, tp->modules, tp_string_atom(tp, "math"), math_mod); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /tinypy/runtime.c: -------------------------------------------------------------------------------- 1 | #include "tp.h" 2 | 3 | #include "runtime/types.c" 4 | #include "runtime/testing.c" 5 | 6 | void tp_save(TP, const char * fname, tp_obj v) { 7 | FILE *f; 8 | f = fopen(fname,"wb"); 9 | if (!f) { 10 | tp_raise(, tp_string_atom(tp, "(tp_save) IOError: ?")); 11 | } 12 | fwrite(tp_string_getptr(v), tp_string_len(v), 1, f); 13 | fclose(f); 14 | } 15 | 16 | tp_obj tp_load(TP, const char * fname) { 17 | FILE *f; 18 | long l; 19 | tp_obj r; 20 | char *s; 21 | struct stat stbuf; 22 | stat(fname, &stbuf); 23 | l = stbuf.st_size; 24 | f = fopen(fname,"rb"); 25 | if (!f) { 26 | tp_raise(tp_None,tp_string_atom(tp, "(tp_load) IOError: ?")); 27 | } 28 | r = tp_string_t(tp, l); 29 | s = tp_string_getptr(r); 30 | fread(s, 1, l, f); 31 | /* if (rr !=l) { printf("hmmn: %d %d\n",rr,(int)l); }*/ 32 | fclose(f); 33 | return r; 34 | } 35 | 36 | 37 | tp_obj tpy_exists(TP) { 38 | char * fname = tp_cstr(tp, TP_PARAMS_STR()); 39 | struct stat stbuf; 40 | tp_obj r = tp_bool(!stat(fname, &stbuf)); 41 | tp_free(tp, fname); 42 | return r; 43 | } 44 | 45 | tp_obj tpy_load(TP) { 46 | char * fname = tp_cstr(tp, TP_PARAMS_STR()); 47 | tp_obj r = tp_load(tp, fname); 48 | tp_free(tp, fname); 49 | return r; 50 | } 51 | 52 | tp_obj tpy_save(TP) { 53 | char * fname = tp_cstr(tp, TP_PARAMS_STR()); 54 | tp_obj v = TP_PARAMS_OBJ(); 55 | tp_save(tp, fname, v); 56 | tp_free(tp, fname); 57 | return tp_None; 58 | } 59 | 60 | 61 | tp_obj tpy_mtime(TP) { 62 | char * fname = tp_cstr(tp, TP_PARAMS_STR()); 63 | struct stat stbuf; 64 | tp_obj r; 65 | if (!stat(fname, &stbuf)) { 66 | tp_free(tp, fname); 67 | r = tp_int(stbuf.st_mtime); 68 | return r; 69 | } else { 70 | tp_free(tp, fname); 71 | tp_raise(tp_None, tp_string_atom(tp, "(tp_mtime) IOError: ?")); 72 | } 73 | } 74 | 75 | 76 | /* Function: tp_system 77 | * 78 | * The system builtin. A grave security flaw. If your version of tinypy 79 | * enables this, you better remove it before deploying your app :P 80 | */ 81 | tp_obj tpy_system(TP) { 82 | char * s = tp_cstr(tp, TP_PARAMS_STR()); 83 | int r = system(s); 84 | tp_free(tp, s); 85 | return tp_int(r); 86 | } 87 | 88 | void tp_module_os_init (TP) { 89 | tp_obj os = tp_object(tp); 90 | tp_set(tp, os, tp_string_atom(tp, "exists"), tp_function(tp, tpy_exists)); 91 | tp_set(tp, os, tp_string_atom(tp, "read"), tp_function(tp, tpy_load)); 92 | tp_set(tp, os, tp_string_atom(tp, "load"), tp_function(tp, tpy_load)); 93 | tp_set(tp, os, tp_string_atom(tp, "system"), tp_function(tp, tpy_system)); 94 | tp_set(tp, os, tp_string_atom(tp, "mtime"), tp_function(tp, tpy_mtime)); 95 | tp_set(tp, os, tp_string_atom(tp, "save"), tp_function(tp, tpy_save)); 96 | tp_set(tp, tp->modules, tp_string_atom(tp, "os"), os); 97 | } 98 | 99 | void tp_module_corelib_init(TP, int enable_py_runtime) { 100 | tp_module_os_init(tp); 101 | 102 | if(enable_py_runtime) { 103 | tp_import_from_buffer(tp, 0, "tinypy.runtime.types", _tp_types_tpc, sizeof(_tp_types_tpc)); 104 | tp_import_from_buffer(tp, 0, "tinypy.runtime.testing", _tp_testing_tpc, sizeof(_tp_testing_tpc)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /java/libtinypy.c: -------------------------------------------------------------------------------- 1 | #define CPYTHON_MOD 2 | #include "../tinypy/tp.c" 3 | #include "../modules/jni/init.c" 4 | 5 | typedef struct { 6 | tp_vm* tp; 7 | tp_obj globals; 8 | } interpreter_t; 9 | 10 | JNIEnv* env = NULL; 11 | interpreter_t* interpreter = NULL; 12 | 13 | tp_obj eval(TP, const char* text, tp_obj globals) { 14 | if(setjmp(tp->nextexpr)) { 15 | --(tp->cur); 16 | tp_print_stack(tp); 17 | return tp->ex; 18 | } 19 | tp_obj code = tp_compile(tp, tp_string_atom(tp, text), tp_string_atom(tp, "")); 20 | tp_set(tp, globals, tp_string_atom(tp, "__name__"), tp_string_atom(tp, "")); 21 | tp_set(tp, globals, tp_string_atom(tp, "__code__"), code); 22 | tp_exec(tp, code, globals); 23 | return tp->last_result; 24 | } 25 | 26 | tp_obj run_file(TP, const char* filename, tp_obj globals) { 27 | if(setjmp(tp->nextexpr)) { 28 | --(tp->cur); 29 | tp_print_stack(tp); 30 | return tp_str(tp, tp->ex); 31 | } 32 | tp_params_v(tp, 1, tp_string_atom(tp, filename)); 33 | tp_obj text = tp_load(tp); 34 | tp_obj code = tp_compile(tp, text, tp_string_atom(tp, "__main__")); 35 | tp_set(tp, globals, tp_string_atom(tp, "__name__"), tp_string_atom(tp, "__main__")); 36 | tp_set(tp, globals, tp_string_atom(tp, "__code__"), code); 37 | tp_exec(tp, code, globals); 38 | return tp->last_result; 39 | } 40 | 41 | void Java_TinyPy_init(JNIEnv* _env, jobject thiz) { 42 | env = _env; 43 | interpreter = malloc(sizeof(interpreter_t)); 44 | 45 | char* argv[] = {"jni"}; 46 | tp_vm* tp = tp_init(1, argv); 47 | interpreter->tp = tp; 48 | interpreter->globals = tp_dict(tp); 49 | jni_init(interpreter->tp); 50 | } 51 | 52 | jstring Java_TinyPy_run(JNIEnv* _env, jobject thiz, jstring filename_) { 53 | env = _env; 54 | if(interpreter) { 55 | const char* filename = (*env)->GetStringUTFChars(env, filename_, NULL); 56 | tp_obj result = run_file(interpreter->tp, filename, interpreter->globals); 57 | result = tp_str(interpreter->tp, result); 58 | return (*env)->NewStringUTF(env, result.string.info->s); 59 | } 60 | return NULL; 61 | } 62 | 63 | jstring Java_TinyPy_eval(JNIEnv* _env, jobject thiz, jstring code_) { 64 | env = _env; 65 | if(interpreter) { 66 | const char* code = (*env)->GetStringUTFChars(env, code_, NULL); 67 | tp_obj result = eval(interpreter->tp, code, interpreter->globals); 68 | result = tp_str(interpreter->tp, result); 69 | return (*env)->NewStringUTF(env, result.string.info->s); 70 | } 71 | return NULL; 72 | } 73 | 74 | jstring Java_TinyPy_getString(JNIEnv* _env, jobject thiz, jstring name_) { 75 | env = _env; 76 | if(interpreter) { 77 | tp_vm* tp = interpreter->tp; 78 | const char* name = (*env)->GetStringUTFChars(env, name_, 0); 79 | if(tp_has(tp, interpreter->globals, tp_string_atom(tp, name)).number.val) { 80 | tp_obj result = tp_get(tp, interpreter->globals, tp_string_atom(tp, name)); 81 | return (*env)->NewStringUTF(env, result.string.info->s); 82 | } else { 83 | fprintf(stderr, "WARNING: variable \"%s\" not found in tinypy globals\n", name); 84 | return NULL; 85 | } 86 | } 87 | return NULL; 88 | } 89 | 90 | void Java_TinyPy_deinit(JNIEnv* _env, jobject thiz) { 91 | env = _env; 92 | if(interpreter) { 93 | tp_deinit(interpreter->tp); 94 | free(interpreter); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tinypy/compiler/__main__.py: -------------------------------------------------------------------------------- 1 | from tinypy.compiler import py2bc 2 | from tinypy.compiler.boot import * 3 | from tinypy.compiler import disasm 4 | from tinypy.compiler import opcodes 5 | 6 | def do_shorts(opts, optstring, shortopts, args): 7 | while optstring != '': 8 | opt, optstring = optstring[0], optstring[1:] 9 | if short_has_arg(opt, shortopts): 10 | if optstring == '': 11 | if not args: 12 | raise Exception('option -%s requires argument' % opt) 13 | optstring, args = args[0], args[1:] 14 | optarg, optstring = optstring, '' 15 | else: 16 | optarg = '' 17 | opts.append(('-' + opt, optarg)) 18 | return opts, args 19 | 20 | def short_has_arg(opt, shortopts): 21 | for i in range(len(shortopts)): 22 | if opt == shortopts[i] != ':': 23 | return shortopts.startswith(':', i+1) 24 | 25 | raise Exception('option -%s not recognized' % opt) 26 | 27 | def getopt(args, shortopts): 28 | opts = [] 29 | while args and args[0].startswith('-') and args[0] != '-': 30 | if args[0] == '--': 31 | args = args[1:] 32 | break 33 | opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:]) 34 | 35 | return opts, args 36 | 37 | def basename(s, stripdir=True): 38 | if stripdir: 39 | for j in range(len(s) - 1, -1, -1): 40 | if j == -1: break 41 | if s[j] == '/': break 42 | else: 43 | j = -1 44 | for i in range(len(s) - 1, 0, -1): 45 | if s[i] == '.': break 46 | return s[j+1:i] 47 | 48 | def main(args=None): 49 | if args is None: args = ARGV 50 | posargs = [] 51 | options = {} 52 | 53 | opts, args = getopt(args[1:], 'cn:o:dx') 54 | opts = dict(opts) 55 | if len(args) == 1: 56 | src = args[0] 57 | out = do_compile(src, opts) 58 | elif '-x' in opts and len(args) == 0: 59 | out = do_opcodes(opts) 60 | else: 61 | print('Usage tinypyc [-c] [-n variable] [-o output_file_name] [-d] src.py') 62 | return 63 | 64 | if '-o' in opts: 65 | dest = opts['-o'] 66 | else: 67 | if '-c' in opts: 68 | dest = basename(src, False) + '.c' 69 | else: 70 | dest = basename(src, False) + '.tpc' 71 | 72 | if dest == '-': 73 | print(out) 74 | else: 75 | save(dest, out) 76 | 77 | def do_opcodes(opts): 78 | return opcodes.create_ccode().encode() 79 | 80 | def do_compile(src, opts): 81 | s = read(src) 82 | data = py2bc.compile(s, src) 83 | if '-d' in opts: 84 | out = disasm.disassemble(data).encode() 85 | elif '-c' in opts: 86 | out = [] 87 | cols = 16 88 | name = opts.get('-n', '_tp_' + basename(src) + '_tpc').encode() 89 | out.append(b"""unsigned char %s[] = {""" % name) 90 | for n in range(0, len(data), cols): 91 | # FIXME: Py2 and Py3 differs in bytes[i]. 92 | # Py2 returns bytes object. b'c', Py3 returns an int (like C) 93 | # we need to properly define what tpy shall do as 94 | # we say in tpy all strings are bytes. 95 | words = [b"0x%02x" % ord(data[i:i+1]) 96 | for i in range(n, min(n + cols, len(data)))] 97 | out.append(b",".join(words) + b",") 98 | 99 | out.append(b"""};""") 100 | out = b'\n'.join(out) 101 | else: 102 | out = data 103 | 104 | return out 105 | 106 | if __name__ == '__main__': 107 | main() 108 | -------------------------------------------------------------------------------- /tinypy/tp_interp.c: -------------------------------------------------------------------------------- 1 | 2 | tp_obj tp_main(TP,char *fname, void *code, int len) { 3 | return tp_import_from_buffer(tp,fname, "__main__", code, len); 4 | } 5 | 6 | tp_obj tp_ez_call(TP, const char *mod, const char *func, tp_obj params) { 7 | tp_obj tmp; 8 | tmp = tp_get(tp,tp->modules,tp_string_atom(tp, mod)); 9 | tmp = tp_get(tp,tmp,tp_string_atom(tp, func)); 10 | return tp_call(tp, tmp, params, tp_None); 11 | } 12 | 13 | /* Function: tp_compile 14 | * Compile some tinypy code. 15 | * 16 | */ 17 | tp_obj tp_compile(TP, tp_obj text, tp_obj fname) { 18 | return tp_ez_call(tp, "tinypy.compiler.py2bc", "compile", tp_params_v(tp, 2, text, fname)); 19 | } 20 | 21 | tp_obj tp_args(TP, int argc, char *argv[]) { 22 | tp_obj self = tp_list_t(tp); 23 | int i; 24 | for (i=1; igcmax = v; 36 | } else { 37 | tp_raise_printf(tp_None, "(tp_conf_set) unknown key %O", &k); 38 | } 39 | return tp_None; 40 | } 41 | 42 | tp_obj tp_conf_get(TP) { 43 | tp_obj o = TP_PARAMS_OBJ(); 44 | tp_obj k = TP_PARAMS_STR(); 45 | if(tp_string_equal_atom(k, "gcmax")) { 46 | return tp_int(tp->gcmax); 47 | } else { 48 | tp_raise_printf(tp_None, "(tp_conf_get) unknown key %O", &k); 49 | } 50 | return tp_None; 51 | } 52 | 53 | tp_obj tp_get_exc(TP) { 54 | tp_obj elems[2] = {*tp->exc, *tp->exc_stack}; 55 | return tp_list_from_items(tp, 2, elems); 56 | } 57 | 58 | tp_obj tpy_exit(TP) { 59 | int code = TP_PARAMS_INT(); 60 | exit(code); 61 | return tp_None; 62 | } 63 | 64 | void tp_module_sys_init (TP, int argc, char * argv[]) { 65 | tp_obj sys = tp_object(tp); 66 | tp_obj args = tp_args(tp,argc,argv); 67 | tp_obj conf = tp_object(tp); 68 | tp_obj conf_class = tp_class(tp); 69 | tp_set(tp, conf_class, tp_string_atom(tp, "__set__"), tp_function(tp, tp_conf_set)); 70 | tp_set(tp, conf_class, tp_string_atom(tp, "__get__"), tp_function(tp, tp_conf_get)); 71 | tp_set_meta(tp, conf, conf_class); 72 | 73 | tp_set(tp, sys, tp_string_atom(tp, "version"), tp_string_atom(tp, "tinypy 1.2+SVN")); 74 | tp_set(tp, sys, tp_string_atom(tp, "modules"), tp->modules); 75 | tp_set(tp, sys, tp_string_atom(tp, "argv"), args); 76 | tp_set(tp, sys, tp_string_atom(tp, "conf"), conf); 77 | tp_set(tp, sys, tp_string_atom(tp, "exit"), tp_function(tp, tpy_exit)); 78 | tp_set(tp, sys, tp_string_atom(tp, "get_exc"), tp_function(tp, tp_get_exc)); 79 | tp_set(tp, tp->modules, tp_string_atom(tp, "sys"), sys); 80 | } 81 | 82 | /* Function: tp_init 83 | * Initializes a new virtual machine. 84 | * 85 | * The given parameters have the same format as the parameters to main, and 86 | * allow passing arguments to your tinypy scripts. 87 | * 88 | * Returns: 89 | * The newly created tinypy instance. 90 | */ 91 | tp_vm * tp_init(int argc, char *argv[], int enable_py_runtime) { 92 | tp_vm *tp = tp_create_vm(); 93 | tp_module_sys_init(tp, argc, argv); 94 | tp_module_builtins_init(tp); 95 | tp_module_corelib_init(tp, enable_py_runtime); 96 | tp_module_compiler_init(tp); 97 | return tp; 98 | } 99 | 100 | 101 | /* Function: tp_deinit 102 | * Destroys a VM instance. 103 | * 104 | * When you no longer need an instance of tinypy, you can use this to free all 105 | * memory used by it. Even when you are using only a single tinypy instance, it 106 | * may be good practice to call this function on shutdown. 107 | */ 108 | void tp_deinit(TP) { 109 | tp_gc_deinit(tp); 110 | tp->mem_used -= sizeof(tp_vm); 111 | free(tp); 112 | } 113 | -------------------------------------------------------------------------------- /tinypy/tp_dict.c: -------------------------------------------------------------------------------- 1 | tp_obj tp_dict_nt(TP) { 2 | tp_obj r = {TP_DICT}; 3 | r.type.magic = TP_DICT_RAW; 4 | r.info = tpd_dict_new(tp); 5 | return r; 6 | } 7 | 8 | /* Function: tp_dict_t 9 | * 10 | * Creates a new dictionary object. 11 | * 12 | * *Note* If you use on the dictionary, you have to use to 13 | * access the "raw" dictionary again. 14 | * 15 | * Returns: 16 | * The newly created dictionary. 17 | */ 18 | 19 | tp_obj tp_dict_t(TP) { 20 | return tp_track(tp, tp_dict_nt(tp)); 21 | } 22 | 23 | /* Function: tp_object 24 | * Creates a new object. 25 | * 26 | * Returns: 27 | * The newly created object. The object initially has no parent class, use 28 | * to set a class. Also see . 29 | */ 30 | tp_obj tp_object(TP) { 31 | tp_obj self = tp_dict_t(tp); 32 | self.type.magic = TP_DICT_OBJECT; 33 | tp_set_meta(tp, self, tp->object_class); 34 | return self; 35 | } 36 | 37 | /* Function: tp_class 38 | * Creates a new base class. 39 | * 40 | * Parameters: 41 | * none 42 | * 43 | * Returns: 44 | * A new, empty class (derived from tinypy's builtin "object" class). 45 | */ 46 | tp_obj tp_class(TP) { 47 | tp_obj klass = tp_dict_t(tp); 48 | klass.type.magic = TP_DICT_CLASS; 49 | tp_set_meta(tp, klass, tp->object_class); 50 | return klass; 51 | } 52 | 53 | tp_obj tp_dict_from_items (TP, int n, tp_obj * argv) { 54 | tp_obj r = tp_dict_t(tp); 55 | int i; for (i=0; iitems = (tpd_item*) tp_malloc(tp, sizeof(tpd_item)*o->alloc); 103 | memcpy(r->items, o->items, sizeof(tpd_item)*o->alloc); 104 | return tp_track(tp, obj); 105 | } 106 | 107 | int tp_dict_equal(TP, tp_obj a, tp_obj b) { 108 | int i; 109 | if(TPD_DICT(a)->len != TPD_DICT(b)->len) { 110 | return 0; 111 | } 112 | for (i=0; ilen; i++) { 113 | int na = tpd_dict_next(tp, TPD_DICT(a)); 114 | tp_obj key = TPD_DICT(a)->items[na].key; 115 | tp_obj value = TPD_DICT(b)->items[na].val; 116 | int hash = tp_hash(tp, key); 117 | 118 | int nb = tpd_dict_hashfind(tp, TPD_DICT(b), hash, key); 119 | if (nb < 0) return 0; 120 | tp_obj bv = tpd_dict_get(tp, TPD_DICT(b), nb); 121 | if(!tp_equal(tp, value, bv)) return 0; 122 | } 123 | return 1; 124 | } 125 | 126 | void tp_dict_update(TP, tp_obj self, tp_obj v) 127 | { 128 | int i; 129 | for (i=0; ilen; i++) { 130 | int n = tpd_dict_next(tp, TPD_DICT(v)); 131 | tp_dict_set(tp, 132 | self, 133 | TPD_DICT(v)->items[n].key, 134 | TPD_DICT(v)->items[n].val); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tinypy/tpy_string.c: -------------------------------------------------------------------------------- 1 | 2 | tp_obj tpy_str_join(TP) { 3 | tp_obj delim = TP_PARAMS_OBJ(); 4 | tp_obj val = TP_PARAMS_OBJ(); 5 | StringBuilder sb[1] = {tp}; 6 | 7 | int l=0,i; 8 | tp_obj r; 9 | char *s; 10 | for (i=0; ilen; i++) { 11 | if (i!=0) { l += tp_string_len(delim); } 12 | l += tp_string_len(tp_str(tp, TPD_LIST(val)->items[i])); 13 | } 14 | r = tp_string_t(tp,l); 15 | s = tp_string_getptr(r); 16 | l = 0; 17 | for (i=0; ilen; i++) { 18 | tp_obj e; 19 | if (i!=0) { 20 | string_builder_write(sb, tp_string_getptr(delim), tp_string_len(delim)); 21 | } 22 | e = tp_str(tp, TPD_LIST(val)->items[i]); 23 | tp_str_internal(tp, TPD_LIST(val)->items[i], sb, 1); 24 | } 25 | return tp_string_steal_from_builder(tp, sb); 26 | } 27 | 28 | tp_obj tpy_str_split(TP) { 29 | tp_obj v = TP_PARAMS_OBJ(); 30 | tp_obj d = TP_PARAMS_OBJ(); 31 | tp_obj r = tp_list_t(tp); 32 | 33 | v = tp_string_view(tp, v, 0, tp_string_len(v)); 34 | 35 | int i; 36 | while ((i = tp_str_index(v, d))!=-1) { 37 | tpd_list_append(tp, TPD_LIST(r), tp_string_view(tp, v, 0, i)); 38 | TPD_STRING(v)->s += i + tp_string_len(d); 39 | TPD_STRING(v)->len -= i + tp_string_len(d); 40 | } 41 | tpd_list_append(tp, TPD_LIST(r), tp_string_view(tp, v, 0, tp_string_len(v))); 42 | return r; 43 | } 44 | 45 | 46 | tp_obj tpy_str_find(TP) { 47 | tp_obj s = TP_PARAMS_OBJ(); 48 | tp_obj v = TP_PARAMS_OBJ(); 49 | return tp_int(tp_str_index(s,v)); 50 | } 51 | 52 | tp_obj tpy_str_index(TP) { 53 | tp_obj s = TP_PARAMS_OBJ(); 54 | tp_obj v = TP_PARAMS_OBJ(); 55 | int n = tp_str_index(s,v); 56 | if (n >= 0) { return tp_int(n); } 57 | tp_raise(tp_None,tp_string_atom(tp, "(tp_str_index) ValueError: substring not found")); 58 | } 59 | 60 | tp_obj tpy_chr(TP) { 61 | int v = TP_PARAMS_INT(); 62 | return tp->chars[(unsigned char)v]; 63 | } 64 | 65 | tp_obj tpy_ord(TP) { 66 | tp_obj s = TP_PARAMS_STR(); 67 | if (tp_string_len(s) != 1) { 68 | tp_raise(tp_None,tp_string_atom(tp, "(tp_ord) TypeError: ord() expected a character")); 69 | } 70 | return tp_int((unsigned char)tp_string_getptr(s)[0]); 71 | } 72 | 73 | // NOTE: tpy strings are all byte strings, like py2. Thus encode is a noop. 74 | tp_obj tpy_str_encode(TP) { 75 | tp_obj o = TP_PARAMS_TYPE(TP_STRING); 76 | return o; 77 | } 78 | 79 | tp_obj tpy_str_strip(TP) { 80 | tp_obj o = TP_PARAMS_TYPE(TP_STRING); 81 | char const *v = tp_string_getptr(o); int l = tp_string_len(o); 82 | int i; int a = l, b = 0; 83 | tp_obj r; 84 | char *s; 85 | for (i=0; i= self.graphics.w or 64 | self.y <= self.graphics.y or self.y >= self.graphics.h): 65 | self.x = self.graphics.center.x - OFFSET + OFFSET * 2 * random.random() 66 | self.y = self.graphics.center.y - OFFSET + OFFSET * 2 * random.random() 67 | self.color[0] = random.randint(20, 255) 68 | self.color[1] = random.randint(20, 255) 69 | self.color[2] = random.randint(20, 255) 70 | else: 71 | gx = self.graphics.center.x + (self.x - self.graphics.center.x) * DIV_FACTOR 72 | if (math.fabs(self.x - self.graphics.center.x) < 1e-6): 73 | gy = self.graphics.center.y + (self.y - self.graphics.center.y) * DIV_FACTOR 74 | else: 75 | k = (gx - self.graphics.center.x) / (self.x - self.graphics.center.x) 76 | gy = self.graphics.center.y + (self.y - self.graphics.center.y) * k 77 | self.x = gx 78 | self.y = gy 79 | 80 | # update self's size 81 | self.size = int(5 * ((math.fabs(self.x - self.graphics.center.x) * 2) / self.graphics.w)) 82 | if self.size <= 1: 83 | self.size = 1 84 | 85 | def main(): 86 | asteroids = [] 87 | for i in range(INIT_NASTEROIDS): 88 | asteroid = Asteroid() 89 | asteroid.update() 90 | asteroids.append(asteroid) 91 | 92 | _quit = False 93 | while not _quit: 94 | for e in pygame.event.get(): 95 | if e.type in (pygame.locals.QUIT, pygame.locals.KEYDOWN): 96 | _quit = True 97 | 98 | if (len(asteroids) < NASTEROIDS): 99 | asteroid = Asteroid() 100 | asteroid.update() 101 | asteroids.append(asteroid) 102 | 103 | for i in range(len(asteroids)): 104 | # hide asteroids[i] 105 | asteroids[i].hide() 106 | 107 | # update asteroids[i] 108 | asteroids[i].update() 109 | 110 | # show asteroids[i] 111 | asteroids[i].show() 112 | 113 | # swap display content actually 114 | Asteroid.graphics.flipDisplay() 115 | 116 | if __name__ == '__main__': 117 | main() 118 | print("#OK") 119 | -------------------------------------------------------------------------------- /tinypy/printf/README.md: -------------------------------------------------------------------------------- 1 | mini-printf 2 | =========== 3 | 4 | Minimal printf() implementation for embedded projects. 5 | 6 | Motivation 7 | ---------- 8 | 9 | I was recently working on an embedded project with a STM32 MCU. 10 | The chip had 32kB of flash memory - that's heaps for a microcontroller! 11 | How surprised I was when the linker suddelnly failed saying that 12 | the program is too big and won't fit! How come?! 13 | 14 | It's just some USB, I2C, GPIO, a few timers ... and snprintf() 15 | It turned out the memory hog was indeed the glibc's snprintf() - it 16 | took nearly 24kB out of my 32kB and left very little for my program. 17 | 18 | Now what? I looked around the internet for some stripped down 19 | printf() implementations but none I really liked. Then I decided 20 | to develop my own minimal snprintf(). 21 | 22 | Here are some numbers (.bin file size of my STM32 project): 23 | 24 | no snprintf(): 10768 bytes 25 | mini snprintf(): 11420 bytes (+ 652 bytes) 26 | glibc snprintf(): 34860 bytes (+24092 bytes!!) 27 | 28 | Why *SN*printf()? 29 | ----------------- 30 | 31 | Why snprintf() and not printf()? Simply because there are so many 32 | different ways to print from an embedded system that I can't really 33 | make an universal-enough printf(). 34 | 35 | The way I chose makes printing really easy - use mini\_snprintf() 36 | to print into a "char buffer[]" and then output that buffer to your 37 | chip's USART, USB or network or whatever other channel you fancy. 38 | 39 | As a by-product there's also a mini\_vsnprintf() function available. 40 | 41 | Compatibility 42 | ------------- 43 | 44 | I didn't implement each and every formatting sequence the glibc does. 45 | For now only these are supported: 46 | 47 | %% - print '%' 48 | %c - character 49 | %s - string 50 | %d, %u - decimal integer 51 | %x, %X - hex integer 52 | 53 | The integer formatting also supports 0-padding up to 9 characters wide. 54 | (no space-padding or left-aligned padding yet). 55 | 56 | The implementation should be compatible with any GCC-based compiler. 57 | Tested with native x86-64 gcc, arm-none-eabi-gcc and avr-gcc. 58 | 59 | It's completely standalone without any external dependencies. 60 | 61 | Usage 62 | ----- 63 | 64 | 1. Include "mini-printf.h" into your source files. 65 | 2. Add mini-printf.o to your objects list. 66 | 3. Use snprintf() as usual in your project. 67 | 4. Compile, Flash, Test 68 | 69 | Etc. 70 | ---- 71 | 72 | Written by: 73 | Michal Ludvig 74 | 75 | Project homepage: 76 | http://logix.cz/michal/devel/mini-printf 77 | 78 | Source download: 79 | https://github.com/mludvig/mini-printf 80 | 81 | Donations: 82 | http://logix.cz/michal/devel/donations 83 | 84 | License 85 | ------- 86 | Copyright (c) 2013,2014 Michal Ludvig 87 | All rights reserved. 88 | 89 | Redistribution and use in source and binary forms, with or without 90 | modification, are permitted provided that the following conditions are met: 91 | * Redistributions of source code must retain the above copyright 92 | notice, this list of conditions and the following disclaimer. 93 | * Redistributions in binary form must reproduce the above copyright 94 | notice, this list of conditions and the following disclaimer in the 95 | documentation and/or other materials provided with the distribution. 96 | * Neither the name of the auhor nor the names of its contributors 97 | may be used to endorse or promote products derived from this software 98 | without specific prior written permission. 99 | 100 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 101 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 102 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 103 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 104 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 105 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 106 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 107 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 108 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 109 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 110 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TINYPYC=./tpc 2 | 3 | GENERATED_SOURCE_FILES= 4 | 5 | RUNTIME_FILES=tinypy/runtime/types.py \ 6 | tinypy/runtime/testing.py 7 | GENERATED_SOURCE_FILES+=$(RUNTIME_FILES:%.py=%.c) 8 | 9 | COMPILER_FILES=tinypy/compiler/boot.py \ 10 | tinypy/compiler/encode.py \ 11 | tinypy/compiler/encode.py \ 12 | tinypy/compiler/parse.py \ 13 | tinypy/compiler/py2bc.py \ 14 | tinypy/compiler/tokenize.py \ 15 | tinypy/compiler/opcodes.py 16 | GENERATED_SOURCE_FILES+=$(COMPILER_FILES:%.py=%.c) 17 | 18 | 19 | TPY_DEP_FILES=tinypy/tp.c \ 20 | tinypy/tp*.c tinypy/tp*.h \ 21 | tinypy/tp_opcodes.h \ 22 | tinypy/printf/*.c tinypy/printf/*.h 23 | 24 | COMPILER_DEP_FILES = tinypy/compiler.c 25 | COMPILER_DEP_FILES+= tinypy/*.h $(COMPILER_FILES:%.py=%.c) 26 | 27 | RUNTIME_DEP_FILES = tinypy/runtime.c 28 | RUNTIME_DEP_FILES+= tinypy/*.h $(RUNTIME_FILES:%.py=%.c) 29 | 30 | VMLIB_FILES=tp.c dummy-compiler.c runtime.c 31 | TPLIB_FILES=tp.c compiler.c runtime.c 32 | 33 | MODULES=math random re 34 | MODULES_A_FILES=$(MODULES:%=modules/%.a) 35 | MODULES_C_FILES=$(MODULES:%=modules/%/init.c) 36 | 37 | TESTS_PY_FILES=$(wildcard tests/*.py) 38 | 39 | # rule to compile python scripts to bytecodes as c source code. 40 | %.c : %.py 41 | @mkdir -p $(dir $@) 42 | $(TINYPYC) -co $@ $^ 43 | 44 | # rule to make objects for static linkage 45 | .objs/%.o : %.c 46 | @mkdir -p $(dir $@) 47 | $(CC) $(CFLAGS) -g -O0 -I . -c -o $@ $< 48 | 49 | .dbgobjs/%.o : %.c 50 | @mkdir -p $(dir $@) 51 | $(CC) $(CFLAGS) -DTPVM_DEBUG -g -O0 -I . -c -o $@ $< 52 | 53 | # rule to make objects for dynamic linkage 54 | .dynobjs/%.o : %.c 55 | @mkdir -p $(dir $@) 56 | $(CC) $(CFLAGS) -fPIC -g -O0 -I . -c -o $@ $< 57 | 58 | 59 | all: tpy tpvm 60 | 61 | debug: tpy-dbg tpvm-dbg 62 | 63 | # do not link libtpy.so by default. 64 | shared: tpy-shared 65 | 66 | # alias 67 | lib: libtpy.so 68 | 69 | modules/modules.c: $(MAKEFILE) 70 | echo "#include " > $@ 71 | for name in $(MODULES); do echo "void $${name}_init(TP);" >> $@; done 72 | echo "void _tp_import_modules(TP) {" >> $@ 73 | for name in $(MODULES); do echo "$${name}_init(tp);" >> $@; done 74 | echo "}" >> $@ 75 | GENERATED_SOURCE_FILES+=modules/modules.c 76 | 77 | modules/modules.a: .objs/modules/modules.o \ 78 | $(MODULES_C_FILES:%.c=.objs/%.o) 79 | rm -f $@ 80 | $(AR) rcu $@ $^ 81 | 82 | tinypy/tp_opcodes.h: tinypy/compiler/opcodes.py 83 | @mkdir -p $(dir $@) 84 | $(TINYPYC) -x -o $@ 85 | GENERATED_SOURCE_FILES+=tinypy/tp_opcodes.h 86 | 87 | # extra dependencies 88 | .objs/tinypy/tp.o : $(TPY_DEP_FILES) 89 | .dbgobjs/tinypy/tp.o : $(TPY_DEP_FILES) 90 | .dynobjs/tinypy/tp.o : $(TPY_DEP_FILES) 91 | .objs/tinypy/compiler.o : $(COMPILER_DEP_FILES) 92 | .dbgobjs/tinypy/compiler.o : $(COMPILER_DEP_FILES) 93 | .dynobjs/tinypy/compiler.o : $(COMPILER_DEP_FILES) 94 | .objs/tinypy/runtime.o : $(RUNTIME_DEP_FILES) 95 | .dbgobjs/tinypy/runtime.o : $(RUNTIME_DEP_FILES) 96 | .dynobjs/tinypy/runtime.o : $(RUNTIME_DEP_FILES) 97 | 98 | # tpvm only takes compiled byte codes (.tpc files) 99 | tpvm : $(VMLIB_FILES:%.c=.objs/tinypy/%.o) .objs/tinypy/vmmain.o modules/modules.a 100 | $(CC) $(CFLAGS) -g -O0 -o $@ $^ -lm 101 | # 102 | # tpvm only takes compiled byte codes (.tpc files) 103 | tpvm-dbg : $(VMLIB_FILES:%.c=.dbgobjs/tinypy/%.o) .dbgobjs/tinypy/vmmain.o modules/modules.a 104 | $(CC) $(CFLAGS) -g -O0 -o $@ $^ -lm 105 | 106 | # tpy takes .py files 107 | tpy : $(TPLIB_FILES:%.c=.objs/tinypy/%.o) .objs/tinypy/tpmain.o modules/modules.a 108 | $(CC) $(CFLAGS) -o $@ $^ -lm 109 | 110 | # tpy takes .py files 111 | tpy-dbg : $(TPLIB_FILES:%.c=.dbgobjs/tinypy/%.o) .dbgobjs/tinypy/tpmain.o modules/modules.a 112 | $(CC) $(CFLAGS) -o $@ $^ -lm 113 | 114 | # rule for making a shared object. This is not universal and shall be adapted. 115 | libtpy.so : $(TPLIB_FILES:%.c=.dynobjs/tinypy/%.o) \ 116 | .dynobjs/modules/modules.o \ 117 | $(MODULES_C_FILES:%.c=.dynobjs/%.o) 118 | $(CC) -shared $(CFLAGS) -lm -Wl,-soname,$(@) -o $@ $^ 119 | #ln -s $@.1 $@ 120 | 121 | # rule for making a shared executable. This is not universal and shall be adapted. 122 | tpy-shared : tinypy/tpmain.c libtpy.so 123 | $(CC) $(CFLAGS) -o $@ $^ -lm -Wl,-rpath,'$$ORIGIN' 124 | 125 | test: $(TESTS_PY_FILES) tpvm run-tests.sh 126 | bash run-tests.sh --backend=tpvm $(TESTS_PY_FILES) 127 | 128 | xtest: $(TESTS_PY_FILES) tpvm tpvm-dbg run-tests.sh 129 | bash run-tests.sh --backend=tpvm --xfail $(TESTS_PY_FILES) 130 | 131 | test-dbg: $(TESTS_PY_FILES) tpvm-dbg run-tests.sh 132 | bash run-tests.sh --backend=tpvm-dbg $(TESTS_PY_FILES) 133 | 134 | test-tpy: $(TESTS_PY_FILES) tpy run-tests.sh 135 | bash run-tests.sh --backend=tpy $(TESTS_PY_FILES) 136 | 137 | test-shared: $(TESTS_PY_FILES) tpy-shared run-tests.sh 138 | bash run-tests.sh --backend=tpy-shared $(TESTS_PY_FILES) 139 | 140 | clean: 141 | rm -rf tpy tpvm tpvm-dbg libtpy.so 142 | rm -rf $(GENERATED_SOURCE_FILES) 143 | rm -rf .objs/ 144 | rm -rf .dbgobjs/ 145 | rm -rf .dynobjs/ 146 | rm -rf modules/*.a 147 | -------------------------------------------------------------------------------- /modules/random/tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import random 4 | #from math import log, exp, sqrt, pi 5 | 6 | def test_seed_state(): 7 | """test seed() and getstate()/setstate() 8 | """ 9 | # random ought to be able to deal with seeds in any form, of follows. 10 | # following code shouldn't cause an exception. 11 | random.seed() 12 | random.seed(0) 13 | random.seed(-1) 14 | random.seed(0.1) 15 | random.seed(-0.1) 16 | random.seed("a") 17 | random.seed("abc") 18 | random.seed("abcd") 19 | random.seed("fasdfasdfasdfadgaldhgldahlgahdlghadlgladh") 20 | random.seed("lxhlh90yowhldshlgah;") 21 | 22 | # state1 and state2 should be different for different seeds 23 | random.seed(1) 24 | state1 = random.getstate() 25 | random.seed(2) 26 | state2 = random.getstate() 27 | rep = 0 28 | for ind in range(len(state1)): 29 | elem1 = state1[ind] 30 | elem2 = state2[ind] 31 | if (elem1 == elem2): rep += 1 32 | if (rep > len(state1) / 2): 33 | print("rep = ", rep, "len(state1) = ", len(state1)) 34 | raise "state1 and state2 should be different" 35 | 36 | # for the same seeds, state1 and state2 should be the same 37 | random.seed(100) 38 | state1 = random.getstate() 39 | random.seed(100) 40 | state2 = random.getstate() 41 | rep = 0 42 | for ind in range(len(state1)): 43 | elem1 = state1[ind] 44 | elem2 = state2[ind] 45 | if (elem1 == elem2): rep += 1 46 | if (rep != len(state1)): 47 | raise "state1 and state2 should be the same" 48 | 49 | def test_jumpahead(): 50 | """jumpahead will change the pseudo-number generator's internal state 51 | """ 52 | random.seed() 53 | state1 = random.getstate() 54 | random.jumpahead(20) 55 | state2 = random.getstate() 56 | rep = 0 57 | for ind in range(len(state1)): 58 | elem1 = state1[ind] 59 | elem2 = state2[ind] 60 | if (elem1 == elem2): rep += 1 61 | if (rep > len(state1) / 2): 62 | raise "state1 and state2 can't be the same" 63 | 64 | def test_setstate(): 65 | """ 66 | """ 67 | random.seed() 68 | oldState = random.getstate() 69 | oldRandSeq = [random.random() for i in range(10)] 70 | random.setstate(oldState) 71 | newRandSeq = [random.random() for i in range(10)] 72 | rep = 0 73 | for ind in range(len(oldRandSeq)): 74 | elem1 = oldRandSeq[ind] 75 | elem2 = newRandSeq[ind] 76 | if (elem1 == elem2): rep += 1 77 | if (rep != len(oldRandSeq)): 78 | raise "oldRandSeq and newRandSeq should be the same" 79 | 80 | def test_random(): 81 | """generate a random number list 82 | """ 83 | x = [random.random() for i in range(100)] 84 | 85 | def test_distribution(): 86 | """these lines are borrowed from python, they shouldn't 87 | cause any exception. 88 | """ 89 | g = random 90 | g.uniform(1,10) 91 | g.paretovariate(1.0) 92 | g.expovariate(1.0) 93 | g.weibullvariate(1.0, 1.0) 94 | g.normalvariate(0.0, 1.0) 95 | g.lognormvariate(0.0, 1.0) 96 | g.vonmisesvariate(0.0, 1.0) 97 | g.gammavariate(0.01, 1.0) 98 | g.gammavariate(1.0, 1.0) 99 | g.gammavariate(200.0, 1.0) 100 | g.betavariate(3.0, 3.0) 101 | 102 | def test_randrange(): 103 | """these input to randrange() shouldn't cause any exception. 104 | """ 105 | random.randrange(100000) 106 | random.randrange(-100000) 107 | random.randrange(0) 108 | random.randrange(-10.2) 109 | 110 | random.randrange(-10, 10) 111 | random.randrange(2, 1000) 112 | random.randrange(0, 1) 113 | random.randrange(-1, 0) 114 | 115 | random.randrange(10, 2000, 2) 116 | random.randrange(-2000, 100, 5) 117 | random.randrange(-1000.3, 1000.7, 2) 118 | 119 | def test_randint(): 120 | """for any valid pair (a, b), randint(a, b) should lay between [a, b] 121 | """ 122 | for i in range(1000): 123 | r = random.randint(-10000, 10000) 124 | if (-10000 <= r <= 10000): continue 125 | else: raise "error: random.randint()" 126 | 127 | def test_choice(): 128 | """random.choice() should be able to deal with string, list. 129 | """ 130 | S = "abcdefg123*@#$%)(" 131 | L = [1, 2, 3, -1, 0.2, -0.1, -10000, "cyc"] 132 | 133 | if random.choice(S) not in S: 134 | raise "error: random.choice(S)" 135 | 136 | if random.choice(L) not in L: 137 | raise "error: random.choice(L)" 138 | 139 | def test_shuffle(): 140 | """test random.shuffle() on list. since string is not writable in-place, 141 | random.shuffle() can not be applied on string. 142 | Note: to copy items from a list to a new list, must use syntax like: 143 | newList = oldList[:] 144 | if use syntax like: newList = oldList, newList is just an alias of oldList. 145 | """ 146 | oldL = [1, 2, 3, -1, 0.2, -0.1, -10000, "cyc"] 147 | newL = oldL[:] 148 | 149 | random.shuffle(newL) 150 | 151 | rep = 0 152 | for ind in range(len(oldL)): 153 | elem1 = oldL[ind] 154 | elem2 = newL[ind] 155 | if (elem1 == elem2): rep += 1 156 | if (rep > len(oldL) / 2): 157 | raise "oldL and newL shouldn't be the same" 158 | 159 | def test_53_bits_per_float(): 160 | pass 161 | 162 | def main(): 163 | test_seed_state() 164 | test_jumpahead() 165 | test_setstate() 166 | test_random() 167 | test_distribution() 168 | test_randrange() 169 | test_randint() 170 | test_choice() 171 | test_shuffle() 172 | test_53_bits_per_float() 173 | print("#OK") 174 | 175 | if __name__ == '__main__': 176 | main() 177 | -------------------------------------------------------------------------------- /tinypy/tp_repr.c: -------------------------------------------------------------------------------- 1 | void tp_str_(TP, tp_obj self, tpd_list * visited, StringBuilder * sb, int mode); 2 | 3 | void tp_str_internal(TP, tp_obj self, StringBuilder * sb, int mode) { 4 | /* we only put unmanaged tp_data objects to the list.*/ 5 | tpd_list * visited = tpd_list_new(tp); 6 | tp_str_(tp, self, visited, sb, mode); 7 | tpd_list_free(tp, visited); 8 | } 9 | 10 | tp_obj tp_str(TP, tp_obj self) { 11 | StringBuilder sb[1] = {tp}; 12 | tp_str_internal(tp, self, sb, 1); 13 | return tp_string_steal_from_builder(tp, sb); 14 | } 15 | tp_obj tp_repr(TP, tp_obj self) { 16 | StringBuilder sb[1] = {tp}; 17 | tp_str_internal(tp, self, sb, 0); 18 | return tp_string_steal_from_builder(tp, sb); 19 | } 20 | 21 | /* Function: tp_str 22 | * String representation of an object. 23 | * Checks for recursive data structures 24 | * 25 | * Returns a string object representating self. 26 | */ 27 | void tp_str_(TP, tp_obj self, tpd_list * visited, StringBuilder * sb, int mode) { 28 | /* if the class has __str__ or __repr__ use those */ 29 | if(mode != 0) { /* str mode */ 30 | TP_META_BEGIN(self, __str__); 31 | tp_obj obj = tp_call(tp, __str__, tp_list_t(tp), tp_None); 32 | string_builder_write(sb, tp_string_getptr(obj), tp_string_len(obj)); 33 | return; 34 | TP_META_END; 35 | } 36 | TP_META_BEGIN(self, __repr__); 37 | tp_obj obj = tp_call(tp, __repr__, tp_list_t(tp), tp_None); 38 | string_builder_write(sb, tp_string_getptr(obj), tp_string_len(obj)); 39 | return; 40 | TP_META_END; 41 | 42 | int type = self.type.typeid; 43 | if(type == TP_DICT) { 44 | tp_obj data = tp_data_nt(tp, 0, TPD_DICT(self)); 45 | /* FIXME: use tp_data_cmp */ 46 | if(tpd_list_find(tp, visited, data, tp_equal) >= 0) { 47 | string_builder_write(sb, "{...}", -1); 48 | return; 49 | } 50 | tpd_list_append(tp, visited, data); 51 | } else if(type == TP_LIST) { 52 | tp_obj data = tp_data_nt(tp, 0, TPD_LIST(self)); 53 | if(tpd_list_find(tp, visited, data, tp_equal) >= 0) { 54 | string_builder_write(sb, "[...]", -1); 55 | return; 56 | } 57 | tpd_list_append(tp, visited, data); 58 | } 59 | 60 | if (type == TP_STRING) { 61 | if(mode != 0) { /* str */ 62 | string_builder_write(sb, tp_string_getptr(self), tp_string_len(self)); 63 | } else { /* repr */ 64 | int i; 65 | string_builder_write(sb, "'", 1); 66 | for (i = 0; i < tp_string_len(self); i ++) { 67 | const char * s = tp_string_getptr(self) + i; 68 | switch(s[0]) { 69 | case '\n': 70 | string_builder_write(sb, "\\n", 2); 71 | break; 72 | case '\r': 73 | string_builder_write(sb, "\\r", 2); 74 | break; 75 | case '\t': 76 | string_builder_write(sb, "\\t", 2); 77 | break; 78 | case '\'': 79 | case '\"': 80 | string_builder_write(sb, "\\", 1); 81 | /* leak through */ 82 | default: 83 | string_builder_write(sb, s, 1); 84 | } 85 | } 86 | string_builder_write(sb, "'", 1); 87 | } 88 | } else if (type == TP_NUMBER) { 89 | char buf[128]; 90 | switch (self.type.magic) { 91 | case TP_NUMBER_INT: 92 | snprintf(buf, 120, "%ld", TPN_AS_INT(self)); 93 | break; 94 | case TP_NUMBER_FLOAT: 95 | snprintf(buf, 120, "%lf", TPN_AS_FLOAT(self)); 96 | break; 97 | default: 98 | abort(); 99 | } 100 | string_builder_write(sb, buf, -1); 101 | } else if(type == TP_DICT) { 102 | if(self.type.magic == TP_DICT_CLASS) { 103 | string_builder_write(sb, "C", -1); 104 | } else if(self.type.magic == TP_DICT_OBJECT) { 105 | string_builder_write(sb, "O", -1); 106 | } else if(self.type.magic == TP_DICT_RAW) { 107 | string_builder_write(sb, "R", -1); 108 | } 109 | string_builder_write(sb, "{", -1); 110 | int i, n = 0; 111 | for(i = 0; i < TPD_DICT(self)->alloc; i++) { 112 | if(TPD_DICT(self)->items[i].used > 0) { 113 | tp_str_(tp, TPD_DICT(self)->items[i].key, visited, sb, mode); 114 | string_builder_write(sb, ": ", -1); 115 | tp_str_(tp, TPD_DICT(self)->items[i].val, visited, sb, mode); 116 | if(n < TPD_DICT(self)->len - 1) { 117 | string_builder_write(sb, ", ", -1); 118 | } 119 | n += 1; 120 | } 121 | } 122 | string_builder_write(sb, "}", -1); 123 | } else if(type == TP_LIST) { 124 | string_builder_write(sb, "[", -1); 125 | int i; 126 | for(i = 0; i < TPD_LIST(self)->len; i++) { 127 | tp_str_(tp, TPD_LIST(self)->items[i], visited, sb, mode); 128 | if(i < TPD_LIST(self)->len - 1) { 129 | string_builder_write(sb, ", ", -1); 130 | } 131 | } 132 | string_builder_write(sb, "]", -1); 133 | } else if (type == TP_NONE) { 134 | string_builder_write(sb, "None", -1); 135 | } else if (type == TP_DATA) { 136 | char buf[128]; 137 | snprintf(buf, 120, "", self.ptr); 138 | string_builder_write(sb, buf, -1); 139 | } else if (type == TP_FUNC) { 140 | char buf[128]; 141 | snprintf(buf, 120, "", TPD_FUNC(self)); 142 | string_builder_write(sb, buf, -1); 143 | } else { 144 | string_builder_write(sb, "", -1); 145 | } 146 | if(type == TP_DICT || type == TP_LIST) { 147 | tpd_list_pop(tp, visited, visited->len - 1, "visited list is empty"); 148 | } 149 | return; 150 | } 151 | 152 | -------------------------------------------------------------------------------- /cpython/cpython.c: -------------------------------------------------------------------------------- 1 | #include "../build/tinypy.c" 2 | #include 3 | #include "structmember.h" 4 | 5 | typedef struct { 6 | PyObject_HEAD 7 | tp_vm *vm; 8 | } TinypyObject; 9 | 10 | static PyObject * 11 | Tinypy_ConvertObj(tp_obj obj) 12 | { 13 | PyObject *TinypyError, *TinypyModule; 14 | 15 | if(!(TinypyModule = PyImport_ImportModule("tinypy"))) { 16 | return NULL; 17 | } 18 | if(!(TinypyError = PyObject_GetAttrString(TinypyModule, "error"))) { 19 | return NULL; 20 | } 21 | if(obj.type.typeid == TP_NUMBER) { 22 | tp_num v = obj.number.val; 23 | if ((fabs(v)-fabs((long)v)) < 0.000001) { 24 | return Py_BuildValue("i", (int) v); 25 | } 26 | else { 27 | return Py_BuildValue("d", v); 28 | } 29 | } 30 | else if(obj.type.typeid == TP_STRING) { 31 | return Py_BuildValue("s#", obj.string.info->s, tp_string_len(obj)); 32 | } 33 | else if(obj.type.typeid == TP_NONE) { 34 | Py_INCREF(Py_None); 35 | return Py_None; 36 | } 37 | else { 38 | PyErr_SetString(TinypyError, "can only return strings, numbers and None"); 39 | return NULL; 40 | } 41 | } 42 | 43 | static int 44 | Tinypy_init(TinypyObject *self, PyObject *args, PyObject *kwds) 45 | { 46 | self->vm = tp_init(0, NULL); 47 | double time = 5*1000; /* 5 seconds default */ 48 | unsigned long mem = 16*1024*1024; /* 16 megabytes default */ 49 | static char *kwlist[] = { "time", "mem", 0 }; 50 | 51 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "|dk", 52 | kwlist, &time, &mem)) { 53 | return -1; 54 | } 55 | tp_sandbox(self->vm, time, mem); 56 | 57 | return 0; 58 | } 59 | 60 | static void 61 | Tinypy_destruct(PyObject *self) 62 | { 63 | tp_deinit(((TinypyObject *) self)->vm); 64 | self->ob_type->tp_free(self); 65 | } 66 | 67 | static PyObject * 68 | Tinypy_exec(TinypyObject *self, PyObject *args, PyObject *kwds) 69 | { 70 | tp_obj obj; 71 | tp_vm *tp = self->vm; 72 | PyObject *ret, *TinypyError, *TinypyModule; 73 | static char *kwlist[] = { "code", "time", 0 }; 74 | const char *code; 75 | 76 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|d", 77 | kwlist, &code, &tp->time_limit)) { 78 | return NULL; 79 | } 80 | 81 | tp->time_elapsed = 0; 82 | tp->mem_exceeded = 0; 83 | if(!(TinypyModule = PyImport_ImportModule("tinypy"))) { 84 | return NULL; 85 | } 86 | if(!(TinypyError = PyObject_GetAttrString(TinypyModule, "error"))) { 87 | return NULL; 88 | } 89 | if(setjmp(tp->nextexpr)) { 90 | --(tp->cur); 91 | PyErr_SetObject(TinypyError, Tinypy_ConvertObj(tp->ex)); 92 | return NULL; 93 | } 94 | tp->clocks = clock(); 95 | obj = tp_eval(tp, code, tp->builtins); 96 | ret = Tinypy_ConvertObj(obj); 97 | return ret; 98 | } 99 | 100 | static PyMethodDef Tinypy_methods[] = { 101 | {"execute", (PyCFunction)Tinypy_exec, METH_VARARGS | METH_KEYWORDS, 102 | "Execute code supplied as the argument on the tinypy interpreter" 103 | }, 104 | {NULL} /* Sentinel */ 105 | }; 106 | 107 | static PyMethodDef tinypy_methods[] = { 108 | {NULL} /* Sentinel */ 109 | }; 110 | 111 | static PyTypeObject TinypyType = { 112 | PyObject_HEAD_INIT(NULL) 113 | 0, /*ob_size*/ 114 | "tinypy.Tinypy", /*tp_name*/ 115 | sizeof(TinypyObject), /*tp_basicsize*/ 116 | 0, /*tpd_itemsize*/ 117 | Tinypy_destruct, /*tp_dealloc*/ 118 | 0, /*tp_print*/ 119 | 0, /*tp_getattr*/ 120 | 0, /*tp_setattr*/ 121 | 0, /*tp_compare*/ 122 | 0, /*tp_repr*/ 123 | 0, /*tp_as_number*/ 124 | 0, /*tp_as_sequence*/ 125 | 0, /*tp_as_mapping*/ 126 | 0, /*tp_hash */ 127 | 0, /*tp_call*/ 128 | 0, /*tp_str*/ 129 | 0, /*tp_getattro*/ 130 | 0, /*tp_setattro*/ 131 | 0, /*tp_as_buffer*/ 132 | Py_TPFLAGS_DEFAULT, /*tp_flags*/ 133 | "Tinypy VM instance", /* tp_doc */ 134 | 0, /* tp_traverse */ 135 | 0, /* tp_clear */ 136 | 0, /* tp_richcompare */ 137 | 0, /* tp_weaklistoffset */ 138 | 0, /* tp_iter */ 139 | 0, /* tp_iternext */ 140 | Tinypy_methods, /* tp_methods */ 141 | 0, /* tp_members */ 142 | 0, /* tp_getset */ 143 | 0, /* tp_base */ 144 | 0, /* tp_dict */ 145 | 0, /* tp_descr_get */ 146 | 0, /* tp_descr_set */ 147 | 0, /* tp_dictoffset */ 148 | (initproc)Tinypy_init, /* tp_init */ 149 | }; 150 | 151 | PyMODINIT_FUNC 152 | inittinypy(void) 153 | { 154 | PyObject *m; 155 | PyObject *TinypyError; 156 | 157 | TinypyType.tp_new = PyType_GenericNew; 158 | if (PyType_Ready(&TinypyType) < 0) { 159 | return; 160 | } 161 | 162 | m = Py_InitModule3("tinypy", tinypy_methods, 163 | "tinypy - a small Python subset interpreter"); 164 | if (m == NULL) { 165 | return; 166 | } 167 | 168 | Py_INCREF(&TinypyType); 169 | if(PyModule_AddObject(m, "Tinypy", (PyObject *)&TinypyType) < 0) { 170 | return; 171 | } 172 | TinypyError = PyErr_NewException("tinypy.error", NULL, NULL); 173 | Py_INCREF(TinypyError); 174 | if(PyModule_AddObject(m, "error", TinypyError) < 0) { 175 | return; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /modules/math/tests.py: -------------------------------------------------------------------------------- 1 | ### This file is grabbed from Python's Lib/test/test_math.py 2 | ###--------------------------------------------------------- 3 | # Python test set -- math module 4 | # XXXX Should not do tests around zero only 5 | 6 | eps = 0.00001 7 | #print('math module, testing with eps ', eps) 8 | import math 9 | 10 | def testit(name, value, expected): 11 | if abs(value-expected) > eps: 12 | msg = name + " returned " + str(value) + " expected " + str(expected) 13 | raise msg 14 | 15 | #print 'constants' 16 | testit('pi', math.pi, 3.1415926) 17 | testit('e', math.e, 2.7182818) 18 | 19 | #print 'acos' 20 | testit('acos(-1)', math.acos(-1), math.pi) 21 | testit('acos(0)', math.acos(0), math.pi/2) 22 | testit('acos(1)', math.acos(1), 0) 23 | 24 | #print 'asin' 25 | testit('asin(-1)', math.asin(-1), -math.pi/2) 26 | testit('asin(0)', math.asin(0), 0) 27 | testit('asin(1)', math.asin(1), math.pi/2) 28 | 29 | #print 'atan' 30 | testit('atan(-1)', math.atan(-1), -math.pi/4) 31 | testit('atan(0)', math.atan(0), 0) 32 | testit('atan(1)', math.atan(1), math.pi/4) 33 | 34 | #print 'atan2' 35 | testit('atan2(-1, 0)', math.atan2(-1, 0), -math.pi/2) 36 | testit('atan2(-1, 1)', math.atan2(-1, 1), -math.pi/4) 37 | testit('atan2(0, 1)', math.atan2(0, 1), 0) 38 | testit('atan2(1, 1)', math.atan2(1, 1), math.pi/4) 39 | testit('atan2(1, 0)', math.atan2(1, 0), math.pi/2) 40 | 41 | #print 'ceil' 42 | testit('ceil(0.5)', math.ceil(0.5), 1) 43 | testit('ceil(1.0)', math.ceil(1.0), 1) 44 | testit('ceil(1.5)', math.ceil(1.5), 2) 45 | testit('ceil(-0.5)', math.ceil(-0.5), 0) 46 | testit('ceil(-1.0)', math.ceil(-1.0), -1) 47 | testit('ceil(-1.5)', math.ceil(-1.5), -1) 48 | 49 | #print 'cos' 50 | testit('cos(-pi/2)', math.cos(-math.pi/2), 0) 51 | testit('cos(0)', math.cos(0), 1) 52 | testit('cos(pi/2)', math.cos(math.pi/2), 0) 53 | testit('cos(pi)', math.cos(math.pi), -1) 54 | 55 | #print 'cosh' 56 | testit('cosh(0)', math.cosh(0), 1) 57 | testit('cosh(2)-2*(cosh(1)**2)', math.cosh(2)-2*(math.cosh(1)**2), -1) # Thanks to Lambert 58 | 59 | #print 'degrees' 60 | testit('degrees(pi)', math.degrees(math.pi), 180.0) 61 | testit('degrees(pi/2)', math.degrees(math.pi/2), 90.0) 62 | testit('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0) 63 | 64 | #print 'exp' 65 | testit('exp(-1)', math.exp(-1), 1/math.e) 66 | testit('exp(0)', math.exp(0), 1) 67 | testit('exp(1)', math.exp(1), math.e) 68 | 69 | #print 'fabs' 70 | testit('fabs(-1)', math.fabs(-1), 1) 71 | testit('fabs(0)', math.fabs(0), 0) 72 | testit('fabs(1)', math.fabs(1), 1) 73 | 74 | #print 'floor' 75 | testit('floor(0.5)', math.floor(0.5), 0) 76 | testit('floor(1.0)', math.floor(1.0), 1) 77 | testit('floor(1.5)', math.floor(1.5), 1) 78 | testit('floor(-0.5)', math.floor(-0.5), -1) 79 | testit('floor(-1.0)', math.floor(-1.0), -1) 80 | testit('floor(-1.5)', math.floor(-1.5), -2) 81 | 82 | #print 'fmod' 83 | testit('fmod(10,1)', math.fmod(10,1), 0) 84 | testit('fmod(10,0.5)', math.fmod(10,0.5), 0) 85 | testit('fmod(10,1.5)', math.fmod(10,1.5), 1) 86 | testit('fmod(-10,1)', math.fmod(-10,1), 0) 87 | testit('fmod(-10,0.5)', math.fmod(-10,0.5), 0) 88 | testit('fmod(-10,1.5)', math.fmod(-10,1.5), -1) 89 | 90 | #print 'frexp' 91 | def testfrexp(name, value, expected): 92 | mant = value[0] 93 | exp = value[1] 94 | emant = expected[0] 95 | eexp = expected[1] 96 | if abs(mant-emant) > eps or exp != eexp: 97 | raise '%s returned (%f, %f), expected (%f, %f)'%\ 98 | (name, mant, exp, emant,eexp) 99 | 100 | testfrexp('frexp(-1)', math.frexp(-1), (-0.5, 1)) 101 | testfrexp('frexp(0)', math.frexp(0), (0, 0)) 102 | testfrexp('frexp(1)', math.frexp(1), (0.5, 1)) 103 | testfrexp('frexp(2)', math.frexp(2), (0.5, 2)) 104 | 105 | #print 'hypot' 106 | testit('hypot(0,0)', math.hypot(0,0), 0) 107 | testit('hypot(3,4)', math.hypot(3,4), 5) 108 | 109 | #print 'ldexp' 110 | testit('ldexp(0,1)', math.ldexp(0,1), 0) 111 | testit('ldexp(1,1)', math.ldexp(1,1), 2) 112 | testit('ldexp(1,-1)', math.ldexp(1,-1), 0.5) 113 | testit('ldexp(-1,1)', math.ldexp(-1,1), -2) 114 | 115 | #print 'log' 116 | testit('log(1/e)', math.log(1/math.e), -1) 117 | testit('log(1)', math.log(1), 0) 118 | testit('log(e)', math.log(math.e), 1) 119 | testit('log(32,2)', math.log(32,2), 5) 120 | testit('log(10**40, 10)', math.log(10**40, 10), 40) 121 | testit('log(10**40, 10**20)', math.log(10**40, 10**20), 2) 122 | 123 | #print 'log10' 124 | testit('log10(0.1)', math.log10(0.1), -1) 125 | testit('log10(1)', math.log10(1), 0) 126 | testit('log10(10)', math.log10(10), 1) 127 | 128 | #print 'modf' 129 | def testmodf(name, value, expected): 130 | v1 = value[0] 131 | v2 = value[1] 132 | e1 = expected[0] 133 | e2 = expected[1] 134 | if abs(v1-e1) > eps or abs(v2-e2): 135 | raise '%s returned (%f, %f), expected (%f, %f)'%\ 136 | (name, v1,v2, e1,e2) 137 | 138 | testmodf('modf(1.5)', math.modf(1.5), (0.5, 1.0)) 139 | testmodf('modf(-1.5)', math.modf(-1.5), (-0.5, -1.0)) 140 | 141 | #print 'pow' 142 | testit('pow(0,1)', math.pow(0,1), 0) 143 | testit('pow(1,0)', math.pow(1,0), 1) 144 | testit('pow(2,1)', math.pow(2,1), 2) 145 | testit('pow(2,-1)', math.pow(2,-1), 0.5) 146 | 147 | #print 'radians' 148 | testit('radians(180)', math.radians(180), math.pi) 149 | testit('radians(90)', math.radians(90), math.pi/2) 150 | testit('radians(-45)', math.radians(-45), -math.pi/4) 151 | 152 | #print 'sin' 153 | testit('sin(0)', math.sin(0), 0) 154 | testit('sin(pi/2)', math.sin(math.pi/2), 1) 155 | testit('sin(-pi/2)', math.sin(-math.pi/2), -1) 156 | 157 | #print 'sinh' 158 | testit('sinh(0)', math.sinh(0), 0) 159 | testit('sinh(1)**2-cosh(1)**2', math.sinh(1)**2-math.cosh(1)**2, -1) 160 | testit('sinh(1)+sinh(-1)', math.sinh(1)+math.sinh(-1), 0) 161 | 162 | #print 'sqrt' 163 | testit('sqrt(0)', math.sqrt(0), 0) 164 | testit('sqrt(1)', math.sqrt(1), 1) 165 | testit('sqrt(4)', math.sqrt(4), 2) 166 | 167 | #print 'tan' 168 | testit('tan(0)', math.tan(0), 0) 169 | testit('tan(pi/4)', math.tan(math.pi/4), 1) 170 | testit('tan(-pi/4)', math.tan(-math.pi/4), -1) 171 | 172 | #print 'tanh' 173 | testit('tanh(0)', math.tanh(0), 0) 174 | testit('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0) 175 | 176 | #print("OK: math module test pass") 177 | -------------------------------------------------------------------------------- /modules/re/regexpr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * -*- mode: c-mode; c-file-style: python -*- 3 | */ 4 | 5 | #ifndef Py_REGEXPR_H 6 | #define Py_REGEXPR_H 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* 12 | * regexpr.h 13 | * 14 | * Author: Tatu Ylonen 15 | * 16 | * Copyright (c) 1991 Tatu Ylonen, Espoo, Finland 17 | * 18 | * Permission to use, copy, modify, distribute, and sell this software 19 | * and its documentation for any purpose is hereby granted without fee, 20 | * provided that the above copyright notice appear in all copies. This 21 | * software is provided "as is" without express or implied warranty. 22 | * 23 | * Created: Thu Sep 26 17:15:36 1991 ylo 24 | * Last modified: Mon Nov 4 15:49:46 1991 ylo 25 | */ 26 | 27 | /* $Id$ */ 28 | 29 | #ifndef REGEXPR_H 30 | #define REGEXPR_H 31 | 32 | #define RE_NREGS 100 /* number of registers available */ 33 | 34 | typedef struct re_pattern_buffer 35 | { 36 | unsigned char *buffer; /* compiled pattern */ 37 | int allocated; /* allocated size of compiled pattern */ 38 | int used; /* actual length of compiled pattern */ 39 | unsigned char *fastmap; /* fastmap[ch] is true if ch can start pattern */ 40 | unsigned char *translate; /* translation to apply during compilation/matching */ 41 | unsigned char fastmap_accurate; /* true if fastmap is valid */ 42 | unsigned char can_be_null; /* true if can match empty string */ 43 | unsigned char uses_registers; /* registers are used and need to be initialized */ 44 | int num_registers; /* number of registers used */ 45 | unsigned char anchor; /* anchor: 0=none 1=begline 2=begbuf */ 46 | } *regexp_t; 47 | 48 | typedef struct re_registers 49 | { 50 | int start[RE_NREGS]; /* start offset of region */ 51 | int end[RE_NREGS]; /* end offset of region */ 52 | } *regexp_registers_t; 53 | 54 | /* bit definitions for syntax */ 55 | #define RE_NO_BK_PARENS 1 /* no quoting for parentheses */ 56 | #define RE_NO_BK_VBAR 2 /* no quoting for vertical bar */ 57 | #define RE_BK_PLUS_QM 4 /* quoting needed for + and ? */ 58 | #define RE_TIGHT_VBAR 8 /* | binds tighter than ^ and $ */ 59 | #define RE_NEWLINE_OR 16 /* treat newline as or */ 60 | #define RE_CONTEXT_INDEP_OPS 32 /* ^$?*+ are special in all contexts */ 61 | #define RE_ANSI_HEX 64 /* ansi sequences (\n etc) and \xhh */ 62 | #define RE_NO_GNU_EXTENSIONS 128 /* no gnu extensions */ 63 | 64 | /* definitions for some common regexp styles */ 65 | #define RE_SYNTAX_AWK (RE_NO_BK_PARENS|RE_NO_BK_VBAR|RE_CONTEXT_INDEP_OPS) 66 | #define RE_SYNTAX_EGREP (RE_SYNTAX_AWK|RE_NEWLINE_OR) 67 | #define RE_SYNTAX_GREP (RE_BK_PLUS_QM|RE_NEWLINE_OR) 68 | #define RE_SYNTAX_EMACS 0 69 | 70 | #define Sword 1 71 | #define Swhitespace 2 72 | #define Sdigit 4 73 | #define Soctaldigit 8 74 | #define Shexdigit 16 75 | 76 | /* Rename all exported symbols to avoid conflicts with similarly named 77 | symbols in some systems' standard C libraries... */ 78 | 79 | #define re_syntax _Py_re_syntax 80 | #define re_syntax_table _Py_re_syntax_table 81 | #define re_compile_initialize _Py_re_compile_initialize 82 | #define re_set_syntax _Py_re_set_syntax 83 | #define re_compile_pattern _Py_re_compile_pattern 84 | #define re_match _Py_re_match 85 | #define re_search _Py_re_search 86 | #define re_compile_fastmap _Py_re_compile_fastmap 87 | #define re_comp _Py_re_comp 88 | #define re_exec _Py_re_exec 89 | 90 | #ifdef HAVE_PROTOTYPES 91 | 92 | extern int re_syntax; 93 | /* This is the actual syntax mask. It was added so that Python could do 94 | * syntax-dependent munging of patterns before compilation. */ 95 | 96 | extern unsigned char re_syntax_table[256]; 97 | 98 | void re_compile_initialize(void); 99 | 100 | int re_set_syntax(int syntax); 101 | /* This sets the syntax to use and returns the previous syntax. The 102 | * syntax is specified by a bit mask of the above defined bits. */ 103 | 104 | char *re_compile_pattern(unsigned char *regex, int regex_size, regexp_t compiled); 105 | /* This compiles the regexp (given in regex and length in regex_size). 106 | * This returns NULL if the regexp compiled successfully, and an error 107 | * message if an error was encountered. The buffer field must be 108 | * initialized to a memory area allocated by malloc (or to NULL) before 109 | * use, and the allocated field must be set to its length (or 0 if 110 | * buffer is NULL). Also, the translate field must be set to point to a 111 | * valid translation table, or NULL if it is not used. */ 112 | 113 | int re_match(regexp_t compiled, unsigned char *string, int size, int pos, 114 | regexp_registers_t old_regs); 115 | /* This tries to match the regexp against the string. This returns the 116 | * length of the matched portion, or -1 if the pattern could not be 117 | * matched and -2 if an error (such as failure stack overflow) is 118 | * encountered. */ 119 | 120 | int re_search(regexp_t compiled, unsigned char *string, int size, int startpos, 121 | int range, regexp_registers_t regs); 122 | /* This searches for a substring matching the regexp. This returns the 123 | * first index at which a match is found. range specifies at how many 124 | * positions to try matching; positive values indicate searching 125 | * forwards, and negative values indicate searching backwards. mstop 126 | * specifies the offset beyond which a match must not go. This returns 127 | * -1 if no match is found, and -2 if an error (such as failure stack 128 | * overflow) is encountered. */ 129 | 130 | void re_compile_fastmap(regexp_t compiled); 131 | /* This computes the fastmap for the regexp. For this to have any effect, 132 | * the calling program must have initialized the fastmap field to point 133 | * to an array of 256 characters. */ 134 | 135 | #else /* HAVE_PROTOTYPES */ 136 | 137 | extern int re_syntax; 138 | extern unsigned char re_syntax_table[256]; 139 | void re_compile_initialize(); 140 | int re_set_syntax(); 141 | char *re_compile_pattern(); 142 | int re_match(); 143 | int re_search(); 144 | void re_compile_fastmap(); 145 | 146 | #endif /* HAVE_PROTOTYPES */ 147 | 148 | #endif /* REGEXPR_H */ 149 | 150 | 151 | 152 | #ifdef __cplusplus 153 | } 154 | #endif 155 | #endif /* !Py_REGEXPR_H */ 156 | -------------------------------------------------------------------------------- /examples/vines.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # 3 | # vines borrowed from xscreensaver 4 | # 5 | """ 6 | /*- 7 | * Copyright (c) 1997 by Tracy Camp campt@hurrah.com 8 | * 9 | * Permission to use, copy, modify, and distribute this software and its 10 | * documentation for any purpose and without fee is hereby granted, 11 | * provided that the above copyright notice appear in all copies and that 12 | * both that copyright notice and this permission notice appear in 13 | * supporting documentation. 14 | * 15 | * This file is provided AS IS with no warranties of any kind. The author 16 | * shall have no liability with respect to the infringement of copyrights, 17 | * trade secrets or any patents by this file or any part thereof. In no 18 | * event will the author be liable for any lost revenue or profits or 19 | * other special, indirect and consequential damages. 20 | * 21 | * If you make a modification I would of course appreciate a copy. 22 | * 23 | * Revision History: 24 | * 01-Nov-2000: Allocation checks 25 | * 11-Jul-1997: David Hansen 26 | * Changed names to vines and modified draw loop 27 | * to honor batchcount so vines can be grown or plotted. 28 | * 10-May-1997: Compatible with xscreensaver 29 | * 21-Mar-1997: David Hansen 30 | * Updated mode to draw complete patterns on every 31 | * iteration instead of growing the vine. Also made 32 | * adjustments to randomization and changed variable 33 | * names to make logic easier to follow. 34 | */ 35 | 36 | /*- 37 | * This was modifed from a 'screen saver' that a friend and I 38 | * wrote on our TI-8x calculators in high school physics one day 39 | * Basically another geometric pattern generator, this ones claim 40 | * to fame is a pseudo-fractal looking vine like pattern that creates 41 | * nifty whorls and loops. 42 | */ 43 | """ 44 | 45 | import sys 46 | import math 47 | import random 48 | import pygame 49 | if "tinypy" not in sys.version: # not tinypy 50 | import pygame.locals 51 | 52 | SCR_WIDTH = 800 53 | SCR_HEIGHT = 600 54 | 55 | class VineStruct(object): 56 | a = 0 57 | x1 = 0 58 | y1 = 0 59 | x2 = 0 60 | y2 = 0 61 | i = 0 62 | length = 0 63 | iterations = 0 64 | constant = 0 65 | ang = 0 66 | centerx = 0 67 | centery = 0 68 | 69 | class Vines(object): 70 | def __init__(self): 71 | self.fp = VineStruct() 72 | self.fp.i = 0 73 | self.fp.length = 0 74 | self.fp.iterations = 30 + random.randint(0, 100) 75 | 76 | pygame.init() 77 | self.screen = pygame.display.set_mode((SCR_WIDTH, SCR_HEIGHT)) 78 | 79 | def __drawLine__(self, x1, y1, x2, y2, color): 80 | 81 | # validate the bounds 82 | if x1 < 0: x1 = 0 83 | if x1 > SCR_WIDTH: x1 = SCR_WIDTH 84 | if x2 < 0: x2 = 0 85 | if x2 > SCR_WIDTH: x2 = SCR_WIDTH 86 | if y1 < 0: y1 = 0 87 | if y1 > SCR_HEIGHT: y1 = SCR_HEIGHT 88 | if y2 < 0: y2 = 0 89 | if y2 > SCR_HEIGHT: y2 = SCR_HEIGHT 90 | 91 | if x1 <= x2: 92 | sx, sy = x1, y1 93 | dx, dy = x2, y2 94 | else: 95 | sx, sy = x2, y2 96 | dx, dy = x1, y1 97 | 98 | if (abs(x1 - x2) < 1e-4): 99 | x = sx 100 | if sy > dy: 101 | sy, dy = dy, sy 102 | y = sy 103 | while (y < dy): 104 | self.screen.set_at((x, y), color) 105 | y += 1 106 | else: 107 | k = (dy - sy) / (dx - sx) 108 | x = sx 109 | while (x < dx): 110 | y = sy + k * (x - sx) 111 | self.screen.set_at((x, y), color) 112 | x += 1 113 | 114 | pygame.display.flip() 115 | 116 | def draw(self): 117 | red = random.randint(0, 255) 118 | green = random.randint(0, 255) 119 | blue = random.randint(0, 255) 120 | if (self.fp.i >= self.fp.length): 121 | self.fp.iterations -= 1 122 | if (self.fp.iterations == 0): 123 | self.__init__(self) 124 | self.fp.centerx = random.randint(0, SCR_WIDTH); 125 | self.fp.centery = random.randint(0, SCR_HEIGHT); 126 | 127 | self.fp.ang = 60 + random.randint(0, 720); 128 | self.fp.length = 100 + random.randint(0, 3000); 129 | self.fp.constant= self.fp.length * (10 + random.randint(0, 10)) 130 | 131 | self.fp.i = 0; 132 | self.fp.a = 0; 133 | self.fp.x1 = 0; 134 | self.fp.y1 = 0; 135 | self.fp.x2 = 1; 136 | self.fp.y2 = 0; 137 | 138 | count = self.fp.i + random.randint(10, 100) 139 | if (count > self.fp.length): 140 | count = self.fp.length 141 | 142 | while (self.fp.i < count): 143 | x1 = self.fp.centerx + (self.fp.x1 / self.fp.constant) 144 | y1 = self.fp.centery - (self.fp.y1 / self.fp.constant) 145 | x2 = self.fp.centerx + (self.fp.x2 / self.fp.constant) 146 | y2 = self.fp.centery - (self.fp.y2 / self.fp.constant) 147 | 148 | color = (red, green, blue) 149 | self.__drawLine__(x1, y1, x2, y2, color) 150 | 151 | self.fp.a += (self.fp.ang * self.fp.i) 152 | self.fp.x1 = self.fp.x2 153 | self.fp.y1 = self.fp.y2 154 | 155 | self.fp.x2 += int((self.fp.i * (math.cos(self.fp.a) * 360.0)) / (2.0 * math.pi)) 156 | self.fp.y2 += int((self.fp.i * (math.sin(self.fp.a) * 360.0)) / (2.0 * math.pi)) 157 | self.fp.i += 1 158 | 159 | def main(): 160 | myVine = Vines() 161 | _quit = False 162 | while not _quit: 163 | for e in pygame.event.get(): 164 | if e.type in (pygame.locals.QUIT,pygame.locals.KEYDOWN): 165 | _quit = True 166 | myVine.draw() 167 | 168 | if __name__ == '__main__': 169 | main() 170 | print("#OK") -------------------------------------------------------------------------------- /modules/pygame/init.c: -------------------------------------------------------------------------------- 1 | #include "SDL.h" 2 | 3 | /* utility functions */ 4 | Uint32 pygame_list_to_color(TP,tp_obj clr,SDL_Surface *s) { 5 | int r,g,b; 6 | r = tp_get(tp,clr,tp_number(0)).number.val; 7 | g = tp_get(tp,clr,tp_number(1)).number.val; 8 | b = tp_get(tp,clr,tp_number(2)).number.val; 9 | return SDL_MapRGB(s->format,r,g,b); 10 | } 11 | 12 | /* surface */ 13 | 14 | #define PYGAME_TYPE_SURF 0x1001 15 | 16 | void pygame_surf_free(TP,tp_obj d) { 17 | if (d.type.magic != PYGAME_TYPE_SURF) { tp_raise(,tp_printf(tp, "%s","not a surface")); } 18 | SDL_FreeSurface((SDL_Surface*)d.data.val); 19 | } 20 | 21 | SDL_Surface *pygame_obj_to_surf(TP,tp_obj self) { 22 | tp_obj d = tp_get(tp,self,tp_string_atom(tp, "__surf")); 23 | if (d.type.magic != PYGAME_TYPE_SURF) { tp_raise(0,tp_printf(tp, "%s","not a surface")); } 24 | return (SDL_Surface*)d.data.val; 25 | } 26 | 27 | 28 | tp_obj pygame_surface_set_at(TP) { 29 | tp_obj self = TP_PARAMS_OBJ(); 30 | tp_obj pos = TP_PARAMS_TYPE(TP_LIST); 31 | tp_obj clr = TP_PARAMS_TYPE(TP_LIST); 32 | SDL_Rect r; 33 | r.x = tp_get(tp,pos,tp_number(0)).number.val; 34 | r.y = tp_get(tp,pos,tp_number(1)).number.val; 35 | r.w = 1; r.h = 1; 36 | SDL_Surface *s =pygame_obj_to_surf(tp,self); 37 | Uint32 c = pygame_list_to_color(tp,clr,s); 38 | SDL_FillRect(s, &r, c); 39 | return tp_None; 40 | } 41 | 42 | 43 | tp_obj pygame_surf_to_obj(TP,SDL_Surface *s) { 44 | tp_obj self = tp_dict(tp); 45 | 46 | tp_obj d = tpy_data(tp,PYGAME_TYPE_SURF,s); 47 | d.data.info->free = pygame_surf_free; 48 | 49 | tp_set(tp,self,tp_string_atom(tp, "__surf"),d); 50 | tp_set(tp,self,tp_string_atom(tp, "set_at"),tp_method(tp,self,pygame_surface_set_at)); 51 | return self; 52 | } 53 | 54 | 55 | 56 | 57 | /* display module */ 58 | 59 | tp_obj pygame_display_set_mode(TP) { 60 | tp_obj sz = TP_PARAMS_TYPE(TP_LIST); 61 | int w = tp_get(tp,sz,tp_number(0)).number.val; 62 | int h = tp_get(tp,sz,tp_number(1)).number.val; 63 | SDL_Surface *s = SDL_SetVideoMode(w, h, 0, 0); 64 | return pygame_surf_to_obj(tp,s); 65 | } 66 | 67 | tp_obj pygame_display_flip(TP) { 68 | SDL_Flip(SDL_GetVideoSurface()); 69 | return tp_None; 70 | } 71 | 72 | SDL_Rect pygame_list_to_rect(TP,tp_obj o) { 73 | SDL_Rect r; 74 | r.x = tp_get(tp,o,tp_number(0)).number.val; 75 | r.y = tp_get(tp,o,tp_number(1)).number.val; 76 | r.w = tp_get(tp,o,tp_number(2)).number.val; 77 | r.h = tp_get(tp,o,tp_number(3)).number.val; 78 | return r; 79 | } 80 | 81 | tp_obj pygame_display_update(TP) { 82 | SDL_Rect r = pygame_list_to_rect(tp,TP_PARAMS_TYPE(TP_LIST)); 83 | SDL_UpdateRects(SDL_GetVideoSurface(), 1, &r); 84 | return tp_None; 85 | } 86 | 87 | /* event module */ 88 | tp_obj pygame_event_get(TP) { 89 | SDL_Event e; 90 | tp_obj r = tp_list(tp); 91 | while (SDL_PollEvent(&e)) { 92 | tp_obj d = tp_dict(tp); 93 | tp_set(tp,d,tp_string_atom(tp, "type"),tp_number(e.type.typeid)); 94 | switch (e.type.typeid) { 95 | case SDL_KEYDOWN: 96 | case SDL_KEYUP: 97 | tp_set(tp,d,tp_string_atom(tp, "key"),tp_number(e.key.keysym.sym)); 98 | tp_set(tp,d,tp_string_atom(tp, "mod"),tp_number(e.key.keysym.mod)); 99 | break; 100 | case SDL_MOUSEMOTION: 101 | tp_set(tp,d,tp_string_atom(tp, "pos"),tp_list_n(tp,2,(tp_obj[]){tp_number(e.motion.x),tp_number(e.motion.y)})); 102 | tp_set(tp,d,tp_string_atom(tp, "rel"),tp_list_n(tp,2,(tp_obj[]){tp_number(e.motion.xrel),tp_number(e.motion.yrel)})); 103 | tp_set(tp,d,tp_string_atom(tp, "state"),tp_number(e.motion.state)); 104 | break; 105 | case SDL_MOUSEBUTTONDOWN: 106 | case SDL_MOUSEBUTTONUP: 107 | tp_set(tp,d,tp_string_atom(tp, "pos"),tp_list_n(tp,2,(tp_obj[]){tp_number(e.button.x),tp_number(e.button.y)})); 108 | tp_set(tp,d,tp_string_atom(tp, "button"),tp_number(e.button.button)); 109 | break; 110 | } 111 | tp_set(tp,r,tp_None,d); 112 | } 113 | return r; 114 | } 115 | 116 | /* mouse */ 117 | tp_obj pygame_mouse_get_pos(TP) { 118 | int x,y; 119 | SDL_GetMouseState(&x,&y); 120 | tp_obj r = tp_list_n(tp,2,(tp_obj[]){tp_number(x),tp_number(y)}); 121 | return r; 122 | } 123 | 124 | /* time */ 125 | tp_obj pygame_time_get_ticks(TP) { 126 | return tp_number(SDL_GetTicks()); 127 | } 128 | 129 | 130 | /* pygame */ 131 | #define PYGAME_LOCALS(a,b) tp_set(tp,m,tp_string_atom(tp, a),tp_number(b)); 132 | 133 | tp_obj _pygame_init(TP) { 134 | SDL_Init(SDL_INIT_VIDEO); 135 | return tp_None; 136 | } 137 | 138 | 139 | void pygame_init(TP) { 140 | tp_obj g,m; 141 | g = tp_dict(tp); 142 | tp_set(tp,tp->modules,tp_string_atom(tp, "pygame"),g); 143 | tp_set(tp,g,tp_string_atom(tp, "init"),tp_func(tp,_pygame_init)); 144 | 145 | /* display */ 146 | m = tp_dict(tp); tp_set(tp,g,tp_string_atom(tp, "display"),m); 147 | tp_set(tp,m,tp_string_atom(tp, "set_mode"),tp_func(tp,pygame_display_set_mode)); 148 | tp_set(tp,m,tp_string_atom(tp, "flip"),tp_func(tp,pygame_display_flip)); 149 | tp_set(tp,m,tp_string_atom(tp, "update"),tp_func(tp,pygame_display_update)); 150 | 151 | /* event */ 152 | m = tp_dict(tp); tp_set(tp,g,tp_string_atom(tp, "event"),m); 153 | tp_set(tp,m,tp_string_atom(tp, "get"),tp_func(tp,pygame_event_get)); 154 | 155 | /* locals */ 156 | m = tp_dict(tp); tp_set(tp,g,tp_string_atom(tp, "locals"),m); 157 | PYGAME_LOCALS("QUIT",SDL_QUIT); 158 | PYGAME_LOCALS("KEYDOWN",SDL_KEYDOWN); 159 | PYGAME_LOCALS("KEYUP",SDL_KEYUP); 160 | PYGAME_LOCALS("MOUSEBUTTONDOWN",SDL_MOUSEBUTTONDOWN); 161 | PYGAME_LOCALS("MOUSEBUTTONUP",SDL_MOUSEBUTTONUP); 162 | PYGAME_LOCALS("MOUSEMOTION",SDL_MOUSEMOTION); 163 | PYGAME_LOCALS("K_UP",SDLK_UP); 164 | PYGAME_LOCALS("K_DOWN",SDLK_DOWN); 165 | PYGAME_LOCALS("K_LEFT",SDLK_LEFT); 166 | PYGAME_LOCALS("K_RIGHT",SDLK_RIGHT); 167 | PYGAME_LOCALS("K_ESCAPE",SDLK_ESCAPE); 168 | PYGAME_LOCALS("K_SPACE",SDLK_SPACE); 169 | PYGAME_LOCALS("K_RETURN",SDLK_RETURN); 170 | 171 | /* mouse */ 172 | m = tp_dict(tp); tp_set(tp,g,tp_string_atom(tp, "mouse"),m); 173 | tp_set(tp,m,tp_string_atom(tp, "get_pos"),tp_func(tp,pygame_mouse_get_pos)); 174 | 175 | /* time */ 176 | m = tp_dict(tp); tp_set(tp,g,tp_string_atom(tp, "time"),m); 177 | tp_set(tp,m,tp_string_atom(tp, "get_ticks"),tp_func(tp,pygame_time_get_ticks)); 178 | 179 | 180 | } 181 | 182 | /**/ 183 | -------------------------------------------------------------------------------- /tinypy/compiler/tokenize.py: -------------------------------------------------------------------------------- 1 | from tinypy.compiler.boot import * 2 | 3 | class Token: 4 | def __init__(self,pos=(0,0),type='symbol',val=None,items=None): 5 | self.pos,self.type,self.val,self.items=pos,type,val,items 6 | 7 | def _format(self): 8 | if self.items is not None: 9 | return str(self.type) + ':[' + ', '.join([i._format() for i in self.items]) + ']' 10 | else: 11 | return str(self.type) + ':' + str(self.val) 12 | def __repr__(self): 13 | return self._format() 14 | 15 | def as_string(self): 16 | if self.type == 'string': 17 | raise 18 | return self 19 | return Token(self.pos, 'string', self.val.encode(), self.items) 20 | 21 | def update(self, other): 22 | for k in other: 23 | self[k] = other[k] 24 | 25 | def u_error(ctx,s,i): 26 | y,x = i 27 | line = s.split('\n')[y-1] 28 | p = '' 29 | if y < 10: p += ' ' 30 | if y < 100: p += ' ' 31 | r = p + str(y) + ": " + line + "\n" 32 | r += " "+" "*x+"^" +'\n' 33 | raise Exception('error: '+ctx+'\n'+r) 34 | 35 | ISYMBOLS = '`-=[];,./~!@$%^&*()+{}:<>?|' 36 | SYMBOLS = [ 37 | 'def','class','yield','return','pass','and','or','not','in','as', 'with', 'import', 38 | 'is','while','break','for','continue','if','else','elif','try', 39 | 'except','raise','assert','True','False','None','global','del','from', 40 | '-','+','*','**','/','%','<<','>>', 41 | '-=','+=','*=','/=','=','==','!=','<','>', '|=', '&=', '^=', 42 | '<=','>=','[',']','{','}','(',')','.',':',',',';','&','|','!', '^' 43 | ] 44 | B_BEGIN,B_END = ['[','(','{'],[']',')','}'] 45 | 46 | class TData: 47 | def __init__(self): 48 | self.y,self.yi,self.nl = 1,0,True 49 | self.res,self.indent,self.braces = [],[0],0 50 | def add(self,t,v): self.res.append(Token(self.f,t,v)) 51 | 52 | def clean(s): 53 | s = s.replace('\r\n','\n') 54 | s = s.replace('\r','\n') 55 | return s 56 | 57 | def tokenize(s): 58 | global T 59 | s = clean(s) 60 | T,i,l = TData(),0,len(s) 61 | try: 62 | return do_tokenize(s,i,l) 63 | except: 64 | u_error('tokenize',s,T.f) 65 | 66 | def do_tokenize(s,i,l): 67 | global T 68 | T.f = (T.y,i-T.yi+1) 69 | while i < l: 70 | c = s[i]; T.f = (T.y,i-T.yi+1) 71 | if T.nl: T.nl = False; i = do_indent(s,i,l) 72 | elif c == '\n': i = do_nl(s,i,l) 73 | elif c in ISYMBOLS: i = do_symbol(s,i,l) 74 | elif c >= '0' and c <= '9': i = do_number(s,i,l) 75 | elif c in 'b' and (s[i+1] =='"' or s[i+1] == "'"): 76 | i += 1 77 | i = do_string(s,i,l) 78 | elif c=='"' or c=="'": i = do_string(s,i,l) 79 | elif (c >= 'a' and c <= 'z') or \ 80 | (c >= 'A' and c <= 'Z') or c == '_': i = do_name(s,i,l) 81 | elif c=='#': i = do_comment(s,i,l) 82 | elif c == '\\' and s[i+1] == '\n': 83 | i += 2; T.y,T.yi = T.y+1,i 84 | elif c == ' ' or c == '\t': i += 1 85 | else: u_error('tokenize',s,T.f) 86 | indent(0) 87 | r = T.res; T = None 88 | # for t in r: 89 | # print (t.pos,t.type,t.val) 90 | return r 91 | 92 | def do_nl(s,i,l): 93 | if not T.braces: 94 | T.add('nl',None) 95 | i,T.nl = i+1,True 96 | T.y,T.yi = T.y+1,i 97 | return i 98 | 99 | def do_indent(s,i,l): 100 | v = 0 101 | while i T.indent[-1]: 111 | T.indent.append(v) 112 | T.add('indent',v) 113 | elif v < T.indent[-1]: 114 | n = T.indent.index(v) 115 | while len(T.indent) > n+1: 116 | v = T.indent.pop() 117 | T.add('dedent',v) 118 | 119 | def do_symbol(s,i,l): 120 | symbols = [] 121 | v,f,i = s[i],i,i+1 122 | if v in SYMBOLS: symbols.append(v) 123 | while i '9') and (c < 'a' or c > 'f') and c != 'x': break 139 | v,i = v+c,i+1 140 | if c == '.': 141 | v,i = v+c,i+1 142 | while i '9': break 145 | v,i = v+c,i+1 146 | T.add('float',v) 147 | else: 148 | T.add('integer',v) 149 | return i 150 | 151 | def do_name(s,i,l): 152 | v,i =s[i],i+1 153 | while i 'z') and (c < 'A' or c > 'Z') and (c < '0' or c > '9') and c != '_': break 156 | v,i = v+c,i+1 157 | if v in SYMBOLS: T.add('symbol',v) 158 | else: T.add('name',v) 159 | return i 160 | 161 | def do_string(s,i,l): 162 | v,q,i = b'',s[i],i+1 163 | if (l-i) >= 5 and s[i] == q and s[i+1] == q: # """ 164 | i += 2 165 | while itp; 14 | if(sb->buffer == NULL) { 15 | sb->buffer = tp_malloc(tp, 128); 16 | sb->len = 0; 17 | sb->size = 128; 18 | } 19 | if(len < 0) len = strlen(s); 20 | if(sb->len + len + 1 >= sb->size) { 21 | sb->size = (sb->len + len + 1) + sb->len / 2; 22 | sb->buffer = tp_realloc(tp, sb->buffer, sb->size); 23 | } 24 | memcpy(sb->buffer + sb->len, s, len); 25 | sb->len += len; 26 | sb->buffer[sb->len] = 0; 27 | } 28 | 29 | void string_builder_echo(StringBuilder * sb, tp_obj o) 30 | { 31 | TP = sb->tp; 32 | o = tp_str(tp, o); 33 | string_builder_write(sb, tp_string_getptr(o), tp_string_len(o)); 34 | } 35 | 36 | 37 | /* File: String 38 | * String handling functions. 39 | */ 40 | 41 | /* 42 | * Create a new empty string of a certain size. 43 | */ 44 | tp_obj tp_string_t(TP, int n) { 45 | tp_obj r; 46 | r.type.typeid = TP_STRING; 47 | r.type.magic = TP_STRING_NORMAL; 48 | r.info = tp_malloc(tp, sizeof(tpd_string)); 49 | TPD_STRING(r)->len = n; 50 | TPD_STRING(r)->s = tp_malloc(tp, n); 51 | return tp_track(tp, r); 52 | } 53 | 54 | tp_obj tp_string_atom(TP, const char * v) { 55 | /* FIXME: use a hash table for the atoms to avoid the leak */ 56 | static tpd_string info = {0}; 57 | tp_obj r; 58 | r.type.typeid = TP_STRING; 59 | r.type.magic = TP_STRING_ATOM; 60 | r.info = &info; 61 | r.ptr = (char*) v; 62 | return r; 63 | } 64 | 65 | /* 66 | * Return a untracked string object from external memory. 67 | */ 68 | tp_obj tp_string_from_const_nt(TP, const char *s, int n) { 69 | tp_obj r; 70 | if(n < 0) n = strlen(s); 71 | r.type.typeid = TP_STRING; 72 | r.type.magic = TP_STRING_EXTERN; 73 | r.info = tp_malloc(tp, sizeof(tpd_string)); 74 | TPD_STRING(r)->base = tp_None; 75 | TPD_STRING(r)->s = (char*) s; 76 | TPD_STRING(r)->len = n; 77 | return r; 78 | } 79 | 80 | /* return a tracked string object from external memory */ 81 | tp_obj tp_string_from_const(TP, const char *s, int n) { 82 | return tp_track(tp, tp_string_from_const_nt(tp, s, n)); 83 | } 84 | 85 | /* 86 | * Create a new string which is a copy of some memory. 87 | */ 88 | tp_obj tp_string_from_buffer(TP, const char *s, int n) { 89 | if(n < 0) n = strlen(s); 90 | tp_obj r = tp_string_t(tp, n); 91 | memcpy(tp_string_getptr(r), s, n); 92 | return r; 93 | } 94 | 95 | tp_obj tp_string_steal_from_builder(TP, StringBuilder * sb) 96 | { 97 | tp_obj r; 98 | r.type.typeid = TP_STRING; 99 | r.type.magic = TP_STRING_NORMAL; 100 | r.info = tp_malloc(tp, sizeof(tpd_string)); 101 | TPD_STRING(r)->len = sb->len; 102 | TPD_STRING(r)->s = sb->buffer; 103 | sb->buffer = NULL; 104 | sb->len = 0; 105 | return tp_track(tp, r); 106 | } 107 | 108 | char * tp_string_getptr(tp_obj s) { 109 | if(s.type.magic == TP_STRING_ATOM) { 110 | return (char*) s.ptr; 111 | } 112 | return TPD_STRING(s)->s; 113 | } 114 | 115 | int tp_string_len(tp_obj s) { 116 | if(s.type.magic == TP_STRING_ATOM) { 117 | return strlen(s.ptr); 118 | } 119 | return TPD_STRING(s)->len; 120 | } 121 | 122 | /* 123 | * Create a new string which is a substring slice (view) of another STRING. 124 | * the returned object does not allocate new memory. It refers to the same 125 | * memory object to the original string. 126 | */ 127 | tp_obj tp_string_view(TP, tp_obj s, int a, int b) { 128 | int l = tp_string_len(s); 129 | a = _tp_max(0,(a<0?l+a:a)); b = _tp_min(l,(b<0?l+b:b)); 130 | tp_obj r; 131 | r.type.typeid = TP_STRING; 132 | r.type.magic = TP_STRING_VIEW; 133 | r.info = tp_malloc(tp, sizeof(tpd_string)); 134 | TPD_STRING(r)->base = s; 135 | TPD_STRING(r)->s = tp_string_getptr(s) + a; 136 | TPD_STRING(r)->len = b - a; 137 | return tp_track(tp, r); 138 | } 139 | 140 | static int _tp_printf_handler(void* tp, void* obj_ptr, int ch, int lenhint, char **buf) 141 | { 142 | tp_obj * obj = obj_ptr; 143 | tp_obj str = (ch == 'O')?tp_str(tp, *obj):tp_repr(tp, *obj); 144 | char * ptr = tp_string_getptr(str); 145 | int len = tp_string_len(str); 146 | if (lenhint > 0 && len > lenhint) len = lenhint; 147 | *buf = malloc(len); 148 | memcpy(*buf, ptr, len); 149 | return len; 150 | } 151 | 152 | static int _tp_printf_puts(char* s, unsigned int len, void * buf) 153 | { 154 | StringBuilder * sb = buf; 155 | string_builder_write(sb, s, len); 156 | return len; 157 | } 158 | static void _tp_printf_freeor(void* tp, void* buf) 159 | { 160 | free(buf); 161 | } 162 | 163 | tp_obj tp_printf(TP, char const *fmt,...) { 164 | va_list arg; 165 | 166 | mini_printf_set_handler(tp, _tp_printf_handler, _tp_printf_freeor); 167 | StringBuilder sb[1] = {tp}; 168 | va_start(arg, fmt); 169 | mini_vpprintf(_tp_printf_puts, sb, fmt, arg); 170 | va_end(arg); 171 | return tp_string_steal_from_builder(tp, sb); 172 | } 173 | 174 | int tp_str_index (tp_obj s, tp_obj k) { 175 | int i=0; 176 | while ((tp_string_len(s) - i) >= tp_string_len(k)) { 177 | if (memcmp(tp_string_getptr(s) + i, 178 | tp_string_getptr(k), 179 | tp_string_len(k)) == 0) { 180 | return i; 181 | } 182 | i += 1; 183 | } 184 | return -1; 185 | } 186 | 187 | int tp_string_cmp_const(tp_obj a, const char * b, int n) 188 | { 189 | int l = _tp_min(tp_string_len(a), n); 190 | int v = memcmp(tp_string_getptr(a), b, l); 191 | if (v == 0) { 192 | v = tp_string_len(a) - n; 193 | } 194 | return v; 195 | } 196 | 197 | int tp_string_cmp(tp_obj a, tp_obj b) 198 | { 199 | int l = _tp_min(tp_string_len(a), tp_string_len(b)); 200 | int v = memcmp(tp_string_getptr(a), tp_string_getptr(b), l); 201 | if (v == 0) { 202 | v = tp_string_len(a) - tp_string_len(b); 203 | } 204 | return v; 205 | } 206 | 207 | tp_obj tp_string_add(TP, tp_obj a, tp_obj b) 208 | { 209 | int al = tp_string_len(a), bl = tp_string_len(b); 210 | tp_obj r = tp_string_t(tp, al+bl); 211 | char *s = tp_string_getptr(r); 212 | memcpy(s, tp_string_getptr(a), al); 213 | memcpy(s + al, tp_string_getptr(b), bl); 214 | return r; 215 | } 216 | 217 | tp_obj tp_string_mul(TP, tp_obj a, int n) 218 | { 219 | int al = tp_string_len(a); 220 | if(n <= 0) { 221 | tp_obj r = tp_string_t(tp, 0); 222 | return r; 223 | } 224 | tp_obj r = tp_string_t(tp, al*n); 225 | char *s = tp_string_getptr(r); 226 | int i; 227 | for (i=0; i 2 | #include 3 | 4 | #define DATA_CLASS 2 5 | #define DATA_METHOD 1 6 | 7 | extern JNIEnv *env; /* this is set elsewhere */ 8 | 9 | typedef struct { 10 | jclass cls; 11 | jmethodID id; 12 | char* name; 13 | char* signature; 14 | } jni_method_t; 15 | 16 | void jni_free_method(TP, tp_obj obj) { 17 | jni_method_t* method = (jni_method_t*) obj.data.val; 18 | //(*env)->DeleteGlobalRef(env, method->id); 19 | free(method->name); 20 | free(method->signature); 21 | free(method); 22 | } 23 | 24 | jvalue jNULL = {.l=NULL}; 25 | 26 | tp_obj jni_find_class(TP) { 27 | tp_obj class_name = TP_PARAMS_STR(); 28 | jclass cls = (*env)->FindClass(env, class_name.string.info->s); 29 | return tpy_data(tp, 0, cls); 30 | } 31 | 32 | tp_obj jni_get_method_id(TP) { 33 | tp_obj cls = TP_PARAMS_TYPE(TP_DATA); 34 | tp_obj name = TP_PARAMS_STR(); 35 | tp_obj signature = TP_PARAMS_STR(); 36 | 37 | jmethodID id = (*env)->GetMethodID(env, cls.data.val, name.string.info->s, signature.string.info->s); 38 | if(id == NULL) return tp_None; 39 | 40 | jni_method_t* method = malloc(sizeof(jni_method_t)); 41 | method->cls = cls.data.val; 42 | method->id = id; //(*env)->NewGlobalRef(env, id); 43 | method->name = strdup(name.string.info->s); 44 | method->signature = strdup(signature.string.info->s); 45 | //(*env)->DeleteLocalRef(env, id); 46 | 47 | tp_obj result = tpy_data(tp, DATA_METHOD, method); 48 | result.data.info->free = jni_free_method; 49 | 50 | return result; 51 | } 52 | 53 | jvalue jni_map_array(TP, const char* type, tp_obj value) { 54 | tp_raise(jNULL, tp_printf(tp, "not implemented")); 55 | } 56 | 57 | tp_obj jni_unmap_array(TP, const char* type, jvalue value) { 58 | tp_raise(tp_None, tp_printf(tp, "not implemented")); 59 | } 60 | 61 | jvalue jni_map_value(TP, const char* type, tp_obj value) { 62 | jvalue result; 63 | switch(*type) { 64 | case 'Z': if(value.type.typeid == TP_NUMBER) { result.z = (int) value.number.val; return result; } break; 65 | case 'B': if(value.type.typeid == TP_NUMBER) { result.b = (unsigned char) value.number.val; return result; } break; 66 | case 'C': if(value.type.typeid == TP_NUMBER) { result.c = (char) value.number.val; return result; } break; 67 | case 'S': if(value.type.typeid == TP_NUMBER) { result.s = (short) value.number.val; return result; } break; 68 | case 'I': if(value.type.typeid == TP_NUMBER) { result.i = (int) value.number.val; return result; } break; 69 | case 'J': if(value.type.typeid == TP_NUMBER) { result.j = (long int) value.number.val; return result; } break; 70 | case 'F': if(value.type.typeid == TP_NUMBER) { result.f = (float) value.number.val; return result; } break; 71 | case 'D': if(value.type.typeid == TP_NUMBER) { result.d = (double) value.number.val; return result; } break; 72 | case 'L': if(value.type.typeid == TP_NONE) { 73 | result.l = NULL; 74 | } else if(!strncmp(type, "Ljava/lang/String;", 18) && value.type.typeid == TP_STRING) { 75 | result.l = (*env)->NewStringUTF(env, value.string.info->s); 76 | return result; 77 | } else if(value.type.typeid == TP_DATA) { 78 | result.l = value.data.val; 79 | return result; 80 | } 81 | break; 82 | if(value.type.typeid == TP_NONE) { 83 | result.l = NULL; 84 | } else if(value.type.typeid == TP_LIST) { 85 | return jni_map_array(tp, type + 1, value); 86 | } 87 | break; 88 | } 89 | tp_raise(jNULL, tp_printf(tp, "unsupported type or type mismatch")); 90 | } 91 | 92 | tp_obj jni_unmap_value(TP, const char* type, jvalue value) { 93 | switch(*type) { 94 | case 'Z': return tp_number(value.z); break; 95 | case 'B': return tp_number(value.b); break; 96 | case 'C': return tp_number(value.c); break; 97 | case 'S': return tp_number(value.s); break; 98 | case 'I': return tp_number(value.i); break; 99 | case 'J': return tp_number(value.j); break; 100 | case 'F': return tp_number(value.f); break; 101 | case 'D': return tp_number(value.d); break; 102 | case 'L': if(value.l == NULL) { 103 | return tp_None; 104 | } else if(!strncmp(type, "Ljava/lang/String;", 18)) { 105 | return tp_string_atom(tp, (*env)->GetStringUTFChars(env, value.l, NULL)); 106 | } else { 107 | return tpy_data(tp, 0, value.l); 108 | } 109 | break; 110 | case '[': return jni_unmap_array(tp, type + 1, value); 111 | } 112 | tp_raise(tp_None, tp_printf(tp, "unsupported type or type mismatch")); 113 | } 114 | 115 | jvalue* jni_convert_args(TP, const char* signature) { 116 | jvalue* arguments = malloc(sizeof(jvalue) * tp->params.list.val->len); 117 | int i = 0; 118 | signature++; 119 | while(*signature != '\0' && *signature != ')') { 120 | if(*signature == '(') { 121 | continue; 122 | } else if(*signature == ')') { 123 | break; 124 | } else { 125 | tp_obj arg = _tp_list_get(tp,tp->params.list.val, i, "convert_args"); 126 | arguments[i] = jni_map_value(tp, signature, arg); 127 | if(*signature == '[') signature++; 128 | else if(*signature == 'L') { 129 | signature++; 130 | while(*signature != ';') signature++; 131 | } 132 | i++; 133 | } 134 | signature++; 135 | } 136 | return arguments; 137 | } 138 | 139 | tp_obj jni_call_object_method(TP) { 140 | tp_obj object = TP_PARAMS_TYPE(TP_DATA); 141 | tp_obj tp_method = TP_PARAMS_TYPE(TP_DATA); 142 | jni_method_t* method = (jni_method_t*) tp_method.data.val; 143 | 144 | jvalue* arguments = jni_convert_args(tp, method->signature); 145 | const char* return_type = strchr(method->signature, ')') + 1; 146 | 147 | jvalue result; 148 | switch(*return_type) { 149 | case 'Z': result.z = (*env)->CallBooleanMethodA(env, object.data.val, method->id, arguments); break; 150 | case 'B': result.b = (*env)->CallByteMethodA(env, object.data.val, method->id, arguments); break; 151 | case 'C': result.c = (*env)->CallCharMethodA(env, object.data.val, method->id, arguments); break; 152 | case 'S': result.s = (*env)->CallShortMethodA(env, object.data.val, method->id, arguments); break; 153 | case 'I': result.i = (*env)->CallIntMethodA(env, object.data.val, method->id, arguments); break; 154 | case 'J': result.j = (*env)->CallLongMethodA(env, object.data.val, method->id, arguments); break; 155 | case 'F': result.f = (*env)->CallFloatMethodA(env, object.data.val, method->id, arguments); break; 156 | case 'D': result.d = (*env)->CallDoubleMethodA(env, object.data.val, method->id, arguments); break; 157 | case 'L': result.l = (*env)->CallObjectMethodA(env, object.data.val, method->id, arguments); break; 158 | case 'V': (*env)->CallVoidMethodA(env, object.data.val, method->id, arguments); free(arguments); return tp_None; break; 159 | default: tp_raise(tp_None, tp_printf(tp, "unsupported return type")); 160 | } 161 | free(arguments); 162 | return jni_unmap_value(tp, return_type, result); 163 | } 164 | 165 | tp_obj jni_new_object(TP) { 166 | tp_obj cls = TP_PARAMS_TYPE(TP_DATA); 167 | tp_obj tp_constructor = TP_PARAMS_TYPE(TP_DATA); 168 | jni_method_t* constructor = (jni_method_t*) tp_constructor.data.val; 169 | 170 | jvalue* arguments = jni_convert_args(tp, constructor->signature); 171 | jobject object = (*env)->NewObjectA(env, cls.data.val, constructor->id, arguments); 172 | 173 | free(arguments); 174 | return tpy_data(tp, 0, object); 175 | } 176 | 177 | /* 178 | * init jni module, namely, set its dictionary 179 | */ 180 | void jni_init(TP) 181 | { 182 | /* 183 | * new a module dict for jni 184 | */ 185 | tp_obj jni_mod = tp_dict(tp); 186 | 187 | tp_set(tp, jni_mod, tp_string_atom(tp, "find_class"), tp_func(tp, jni_find_class)); 188 | tp_set(tp, jni_mod, tp_string_atom(tp, "get_method_id"), tp_func(tp, jni_get_method_id)); 189 | tp_set(tp, jni_mod, tp_string_atom(tp, "get_static_method_id"), tp_func(tp, jni_get_method_id)); 190 | tp_set(tp, jni_mod, tp_string_atom(tp, "call_object_method"), tp_func(tp, jni_call_object_method)); 191 | tp_set(tp, jni_mod, tp_string_atom(tp, "new_object"), tp_func(tp, jni_new_object)); 192 | tp_set(tp, jni_mod, tp_string_atom(tp, "find_class"), tp_func(tp, jni_find_class)); 193 | 194 | /* 195 | * bind special attributes to jni module 196 | */ 197 | tp_set(tp, jni_mod, tp_string_atom(tp, "__doc__"), tp_string_atom(tp, "This module gives access to java classes.")); 198 | tp_set(tp, jni_mod, tp_string_atom(tp, "__name__"), tp_string_atom(tp, "jni")); 199 | tp_set(tp, jni_mod, tp_string_atom(tp, "__file__"), tp_string_atom(tp, __FILE__)); 200 | 201 | /* 202 | * bind to tiny modules[] 203 | */ 204 | tp_set(tp, tp->modules, tp_string_atom(tp, "jni"), jni_mod); 205 | } 206 | 207 | -------------------------------------------------------------------------------- /tinypy/printf/mini-printf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The Minimal snprintf() implementation 3 | * 4 | * Copyright (c) 2013,2014 Michal Ludvig 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of the auhor nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | * ---- 30 | * 31 | * This is a minimal snprintf() implementation optimised 32 | * for embedded systems with a very limited program memory. 33 | * mini_snprintf() doesn't support _all_ the formatting 34 | * the glibc does but on the other hand is a lot smaller. 35 | * Here are some numbers from my STM32 project (.bin file size): 36 | * no snprintf(): 10768 bytes 37 | * mini snprintf(): 11420 bytes (+ 652 bytes) 38 | * glibc snprintf(): 34860 bytes (+24092 bytes) 39 | * Wasting nearly 24kB of memory just for snprintf() on 40 | * a chip with 32kB flash is crazy. Use mini_snprintf() instead. 41 | * 42 | */ 43 | 44 | #include "mini-printf.h" 45 | 46 | static unsigned int 47 | mini_strlen(const char *s) 48 | { 49 | unsigned int len = 0; 50 | while (s[len] != '\0') len++; 51 | return len; 52 | } 53 | 54 | static unsigned int 55 | mini_itoa(long value, unsigned int radix, unsigned int uppercase, unsigned int unsig, 56 | char *buffer) 57 | { 58 | char *pbuffer = buffer; 59 | int negative = 0; 60 | unsigned int i, len; 61 | 62 | /* No support for unusual radixes. */ 63 | if (radix > 16) 64 | return 0; 65 | 66 | if (value < 0 && !unsig) { 67 | negative = 1; 68 | value = -value; 69 | } 70 | 71 | /* This builds the string back to front ... */ 72 | do { 73 | int digit = value % radix; 74 | *(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10); 75 | value /= radix; 76 | } while (value > 0); 77 | 78 | if (negative) 79 | *(pbuffer++) = '-'; 80 | 81 | *(pbuffer) = '\0'; 82 | 83 | /* ... now we reverse it (could do it recursively but will 84 | * conserve the stack space) */ 85 | len = (pbuffer - buffer); 86 | for (i = 0; i < len / 2; i++) { 87 | char j = buffer[i]; 88 | buffer[i] = buffer[len-i-1]; 89 | buffer[len-i-1] = j; 90 | } 91 | 92 | return len; 93 | } 94 | 95 | static unsigned int 96 | mini_pad(char* ptr, unsigned int len, char pad_char, unsigned int pad_to, char *buffer) 97 | { 98 | int i; 99 | int overflow = 0; 100 | char * pbuffer = buffer; 101 | if(pad_to == 0) pad_to = len; 102 | if(len > pad_to) { 103 | len = pad_to; 104 | overflow = 1; 105 | } 106 | for(i = pad_to - len; i > 0; i --) { 107 | *(pbuffer++) = pad_char; 108 | } 109 | for(i = len; i > 0; i --) { 110 | *(pbuffer++) = *(ptr++); 111 | } 112 | len = pbuffer - buffer; 113 | if(overflow) { 114 | for (i = 0; i < 3 && pbuffer > buffer; i ++) { 115 | *(pbuffer-- - 1) = '*'; 116 | } 117 | } 118 | return len; 119 | } 120 | 121 | struct mini_buff { 122 | char *buffer, *pbuffer; 123 | unsigned int buffer_len; 124 | }; 125 | 126 | static int 127 | _puts(char *s, unsigned int len, void *buf) 128 | { 129 | if(!buf) return len; 130 | struct mini_buff *b = buf; 131 | char * p0 = b->buffer; 132 | unsigned int i; 133 | /* Copy to buffer */ 134 | for (i = 0; i < len; i++) { 135 | if(b->pbuffer == b->buffer + b->buffer_len - 1) { 136 | break; 137 | } 138 | *(b->pbuffer ++) = s[i]; 139 | } 140 | *(b->pbuffer) = 0; 141 | return b->pbuffer - p0; 142 | } 143 | 144 | #ifdef MINI_PRINTF_ENABLE_OBJECTS 145 | static int (*mini_handler) (void* data, void* obj, int ch, int lhint, char** bf) = 0; 146 | static void (*mini_handler_freeor)(void* data, void*) = 0; 147 | static void * mini_handler_data = 0; 148 | 149 | void mini_printf_set_handler( 150 | void* data, 151 | int (*handler)(void* data, void* obj, int ch, int len_hint, char** buf), 152 | void (*freeor)(void* data, void* buf)) 153 | { 154 | mini_handler = handler; 155 | mini_handler_freeor = freeor; 156 | mini_handler_data = data; 157 | } 158 | #endif 159 | 160 | int 161 | mini_vsnprintf(char *buffer, unsigned int buffer_len, const char *fmt, va_list va) 162 | { 163 | struct mini_buff b; 164 | b.buffer = buffer; 165 | b.pbuffer = buffer; 166 | b.buffer_len = buffer_len; 167 | if(buffer_len == 0) buffer = (void*) 0; 168 | int n = mini_vpprintf(_puts, (buffer != (void*)0)?&b:(void*)0, fmt, va); 169 | if(buffer == (void*) 0) { 170 | return n; 171 | } 172 | return b.pbuffer - b.buffer; 173 | } 174 | 175 | int 176 | mini_vpprintf(int (*puts)(char* s, unsigned int len, void* buf), void* buf, const char *fmt, va_list va) 177 | { 178 | char bf[24]; 179 | char bf2[24]; 180 | char ch; 181 | #ifdef MINI_PRINTF_ENABLE_OBJECTS 182 | void* obj; 183 | #endif 184 | if(puts == (void*)0) { 185 | /* run puts in counting mode. */ 186 | puts = _puts; buf = (void*)0; 187 | } 188 | unsigned int n = 0; 189 | while ((ch=*(fmt++))) { 190 | unsigned int len; 191 | if (ch!='%') { 192 | len = 1; 193 | len = puts(&ch, len, buf); 194 | } else { 195 | char pad_char = ' '; 196 | unsigned int pad_to = 0; 197 | char l = 0; 198 | char *ptr; 199 | 200 | ch=*(fmt++); 201 | 202 | /* Zero padding requested */ 203 | if (ch == '0') pad_char = '0'; 204 | while (ch >= '0' && ch <= '9') { 205 | pad_to = pad_to * 10 + (ch - '0'); 206 | ch=*(fmt++); 207 | } 208 | if(pad_to > sizeof(bf)) { 209 | pad_to = sizeof(bf); 210 | } 211 | if (ch == 'l') { 212 | l = 1; 213 | ch=*(fmt++); 214 | } 215 | 216 | switch (ch) { 217 | case 0: 218 | goto end; 219 | case 'u': 220 | case 'd': 221 | if(l) { 222 | len = mini_itoa(va_arg(va, unsigned long), 10, 0, (ch=='u'), bf2); 223 | } else { 224 | if(ch == 'u') { 225 | len = mini_itoa((unsigned long) va_arg(va, unsigned int), 10, 0, 1, bf2); 226 | } else { 227 | len = mini_itoa((long) va_arg(va, int), 10, 0, 0, bf2); 228 | } 229 | } 230 | len = mini_pad(bf2, len, pad_char, pad_to, bf); 231 | len = puts(bf, len, buf); 232 | break; 233 | 234 | case 'x': 235 | case 'X': 236 | if(l) { 237 | len = mini_itoa(va_arg(va, unsigned long), 16, (ch=='X'), 1, bf2); 238 | } else { 239 | len = mini_itoa((unsigned long) va_arg(va, unsigned int), 16, (ch=='X'), 1, bf2); 240 | } 241 | len = mini_pad(bf2, len, pad_char, pad_to, bf); 242 | len = puts(bf, len, buf); 243 | break; 244 | 245 | case 'c' : 246 | ch = (char)(va_arg(va, int)); 247 | len = mini_pad(&ch, 1, pad_char, pad_to, bf); 248 | len = puts(bf, len, buf); 249 | break; 250 | 251 | case 's' : 252 | ptr = va_arg(va, char*); 253 | len = mini_strlen(ptr); 254 | if (pad_to > 0) { 255 | len = mini_pad(ptr, len, pad_char, pad_to, bf); 256 | len = puts(bf, len, buf); 257 | } else { 258 | len = puts(ptr, len, buf); 259 | } 260 | break; 261 | #ifdef MINI_PRINTF_ENABLE_OBJECTS 262 | case 'O' : /* Object by content (e.g. str) */ 263 | case 'R' : /* Object by representation (e.g. repr)*/ 264 | obj = va_arg(va, void*); 265 | len = mini_handler(mini_handler_data, obj, ch, pad_to, &ptr); 266 | if (pad_to > 0) { 267 | len = mini_pad(ptr, len, pad_char, pad_to, bf); 268 | len = puts(bf, len, buf); 269 | } else { 270 | len = puts(ptr, len, buf); 271 | } 272 | mini_handler_freeor(mini_handler_data, ptr); 273 | break; 274 | #endif 275 | default: 276 | len = 1; 277 | len = puts(&ch, len, buf); 278 | break; 279 | } 280 | } 281 | n = n + len; 282 | } 283 | end: 284 | return n; 285 | } 286 | 287 | 288 | int 289 | mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...) 290 | { 291 | int ret; 292 | va_list va; 293 | va_start(va, fmt); 294 | ret = mini_vsnprintf(buffer, buffer_len, fmt, va); 295 | va_end(va); 296 | 297 | return ret; 298 | } 299 | 300 | int 301 | mini_pprintf(int (*puts)(char*s, unsigned int len, void* buf), void* buf, const char *fmt, ...) 302 | { 303 | int ret; 304 | va_list va; 305 | va_start(va, fmt); 306 | ret = mini_vpprintf(puts, buf, fmt, va); 307 | va_end(va); 308 | 309 | return ret; 310 | } 311 | 312 | --------------------------------------------------------------------------------