diff --git a/README.txt b/README.txt index 954142f..dd59728 100644 --- a/README.txt +++ b/README.txt @@ -2,6 +2,10 @@ "batteries not (yet) included" Copyright (c) 2008 Phil Hassey +This version adds: +- dl module +- jni module + java interface + Check it out: $ python setup.py linux pygame @@ -59,4 +63,4 @@ A. Ask me some other time. Short answer: I do it like lua does it. Only calls Q. At the end of build.py tinypy doesn't work! A. This is probably because of my use of -O3 in the final step. Run the command - again without -O3. Some versions of GCC are buggy and don't do well with it. \ No newline at end of file + again without -O3. Some versions of GCC are buggy and don't do well with it. diff --git a/java/Makefile b/java/Makefile new file mode 100644 index 0000000..b7e3d41 --- /dev/null +++ b/java/Makefile @@ -0,0 +1,13 @@ +CFLAGS=-fPIC -I$(JAVA_HOME)/../include/ -I$(JAVA_HOME)/../include/linux -pipe -g -Wall + +run: TinyPy.class libtinypy.so + java -Djava.library.path=. -cp . TinyPy + +%.class: %.java + javac $< + +libtinypy.so: libtinypy.c + $(CC) -o $@ -shared -Wl,-soname,$@ $^ $(CFLAGS) + +clean: + rm -f *.log TinyPy.class libtinypy.so diff --git a/java/TinyPy.java b/java/TinyPy.java new file mode 100644 index 0000000..75738ed --- /dev/null +++ b/java/TinyPy.java @@ -0,0 +1,39 @@ +import java.util.Scanner; + +class TinyPy { + public static void main(String [] args) { + init(); + run("test.py"); + say(getString("foo")); + say(eval("say")); + deinit(); + } + + static void gdb() { + System.out.println("enter number when gdb is ready"); + Scanner sc = new Scanner(System.in); + int intNum = sc.nextInt(); + } + + void say2(String message) { + System.out.println(message); + } + + static void say(String message) { + System.out.println(message); + } + + static public native void init(); + static public native String run(String filename); + static public native String eval(String code); + static public native void deinit(); + + static public native String getString(String name); + static public native void call(); + + static public native long getPyObject(String name); + + static { + System.loadLibrary("tinypy"); + } +} diff --git a/java/libtinypy.c b/java/libtinypy.c new file mode 100644 index 0000000..b98d321 --- /dev/null +++ b/java/libtinypy.c @@ -0,0 +1,96 @@ +#define CPYTHON_MOD +#include "../tinypy/tp.c" +#include "../modules/jni/init.c" + +typedef struct { + tp_vm* tp; + tp_obj globals; +} interpreter_t; + +JNIEnv* env = NULL; +interpreter_t* interpreter = NULL; + +tp_obj eval(TP, const char* text, tp_obj globals) { + if(setjmp(tp->nextexpr)) { + --(tp->cur); + tp_print_stack(tp); + return tp->ex; + } + tp_obj code = tp_compile(tp, tp_string(text), tp_string("")); + tp_set(tp, globals, tp_string("__name__"), tp_string("")); + tp_set(tp, globals, tp_string("__code__"), code); + tp_exec(tp, code, globals); + return tp->last_result; +} + +tp_obj run_file(TP, const char* filename, tp_obj globals) { + if(setjmp(tp->nextexpr)) { + --(tp->cur); + tp_print_stack(tp); + return tp_str(tp, tp->ex); + } + tp_params_v(tp, 1, tp_string(filename)); + tp_obj text = tp_load(tp); + tp_obj code = tp_compile(tp, text, tp_string("__main__")); + tp_set(tp, globals, tp_string("__name__"), tp_string("__main__")); + tp_set(tp, globals, tp_string("__code__"), code); + tp_exec(tp, code, globals); + return tp->last_result; +} + +void Java_TinyPy_init(JNIEnv* _env, jobject thiz) { + env = _env; + interpreter = malloc(sizeof(interpreter_t)); + + char* argv[] = {"jni"}; + tp_vm* tp = tp_init(1, argv); + interpreter->tp = tp; + interpreter->globals = tp_dict(tp); + jni_init(interpreter->tp); +} + +jstring Java_TinyPy_run(JNIEnv* _env, jobject thiz, jstring filename_) { + env = _env; + if(interpreter) { + const char* filename = (*env)->GetStringUTFChars(env, filename_, NULL); + tp_obj result = run_file(interpreter->tp, filename, interpreter->globals); + result = tp_str(interpreter->tp, result); + return (*env)->NewStringUTF(env, result.string.val); + } + return NULL; +} + +jstring Java_TinyPy_eval(JNIEnv* _env, jobject thiz, jstring code_) { + env = _env; + if(interpreter) { + const char* code = (*env)->GetStringUTFChars(env, code_, NULL); + tp_obj result = eval(interpreter->tp, code, interpreter->globals); + result = tp_str(interpreter->tp, result); + return (*env)->NewStringUTF(env, result.string.val); + } + return NULL; +} + +jstring Java_TinyPy_getString(JNIEnv* _env, jobject thiz, jstring name_) { + env = _env; + if(interpreter) { + tp_vm* tp = interpreter->tp; + const char* name = (*env)->GetStringUTFChars(env, name_, 0); + if(tp_has(tp, interpreter->globals, tp_string(name)).number.val) { + tp_obj result = tp_get(tp, interpreter->globals, tp_string(name)); + return (*env)->NewStringUTF(env, result.string.val); + } else { + fprintf(stderr, "WARNING: variable \"%s\" not found in tinypy globals\n", name); + return NULL; + } + } + return NULL; +} + +void Java_TinyPy_deinit(JNIEnv* _env, jobject thiz) { + env = _env; + if(interpreter) { + tp_deinit(interpreter->tp); + free(interpreter); + } +} diff --git a/java/test.py b/java/test.py new file mode 100644 index 0000000..d2243f0 --- /dev/null +++ b/java/test.py @@ -0,0 +1,9 @@ +import jni + +TinyPy = jni.find_class("TinyPy") +constructor = jni.get_method_id(TinyPy, "", "()V") +tp = jni.new_object(TinyPy, constructor) +say = jni.get_method_id(TinyPy, "say2", "(Ljava/lang/String;)V") +jni.call_object_method(tp, say, "hello world") + +foo = "hi there" diff --git a/modules/dl/init.c b/modules/dl/init.c new file mode 100644 index 0000000..b433cab --- /dev/null +++ b/modules/dl/init.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include + +#define VOID_T 'V' +#define UINT8_T '8' +#define SINT8_T '9' +#define UINT16_T '6' +#define SINT16_T '7' +#define UINT32_T '2' +#define SINT32_T '3' +#define UINT64_T '4' +#define SINT64_T '5' +#define FLOAT_T 'f' +#define DOUBLE_T 'd' +#define UCHAR_T 'c' +#define SCHAR_T 'C' +#define USHORT_T 't' +#define SSHORT_T 'T' +#define UINT_T 'i' +#define SINT_T 'I' +#define ULONG_T 'l' +#define SLONG_T 'L' +#define LDOUBLE_T 'D' +#define STRING_T 'S' +#define POINTER_T '*' + +int type_size(TP, char type) { + switch(type) { + case VOID_T: return 0; break; + case UINT8_T: + case SINT8_T: return sizeof(int8_t); break; + case UINT16_T: + case SINT16_T: return sizeof(int16_t); break; + case UINT32_T: + case SINT32_T: return sizeof(int32_t); break; + case UINT64_T: + case SINT64_T: return sizeof(int64_t); break; + case FLOAT_T: return sizeof(float); break; + case DOUBLE_T: return sizeof(double); break; + case UCHAR_T: + case SCHAR_T: return sizeof(char); break; + case USHORT_T: + case SSHORT_T: return sizeof(short); break; + case UINT_T: + case SINT_T: return sizeof(int); break; + case ULONG_T: + case SLONG_T: return sizeof(long int); break; + case LDOUBLE_T: return sizeof(long double); break; + case STRING_T: + case POINTER_T: return sizeof(void*); break; + default: tp_raise(-1, tp_printf(tp, "invalid type \"%c\"")); + } +} + +ffi_type* map_type(TP, char type) { + switch(type) { + case VOID_T: return &ffi_type_void; break; + case UINT8_T: return &ffi_type_uint8; break; + case SINT8_T: return &ffi_type_sint8; break; + case UINT16_T: return &ffi_type_uint16; break; + case SINT16_T: return &ffi_type_sint16; break; + case UINT32_T: return &ffi_type_uint32; break; + case SINT32_T: return &ffi_type_sint32; break; + case UINT64_T: return &ffi_type_uint64; break; + case SINT64_T: return &ffi_type_sint64; break; + case FLOAT_T: return &ffi_type_float; break; + case DOUBLE_T: return &ffi_type_double; break; + case UCHAR_T: return &ffi_type_uchar; break; + case SCHAR_T: return &ffi_type_schar; break; + case USHORT_T: return &ffi_type_ushort; break; + case SSHORT_T: return &ffi_type_sshort; break; + case UINT_T: return &ffi_type_uint; break; + case SINT_T: return &ffi_type_sint; break; + case ULONG_T: return &ffi_type_ulong; break; + case SLONG_T: return &ffi_type_slong; break; + case LDOUBLE_T: return &ffi_type_longdouble; break; + case STRING_T: + case POINTER_T: return &ffi_type_pointer; break; + default: tp_raise(&ffi_type_void, tp_printf(tp, "invalid type \"%c\"")); + } +} + +#define _map_value(type, value) result = malloc(sizeof(type)); *((type*)result) = (type) value; break +void* map_value(TP, char type, tp_obj value) { + void* result = NULL; + switch(type) { + case UCHAR_T: + case UINT8_T: _map_value(uint8_t, value.number.val); + case SCHAR_T: + case SINT8_T: _map_value(int8_t, value.number.val); + case USHORT_T: + case UINT16_T: _map_value(uint16_t, value.number.val); + case SSHORT_T: + case SINT16_T: _map_value(int16_t, value.number.val); + case UINT_T: + case UINT32_T: _map_value(uint32_t, value.number.val); + case SINT_T: + case SINT32_T: _map_value(int32_t, value.number.val); + case ULONG_T: + case UINT64_T: _map_value(uint64_t, value.number.val); + case SLONG_T: + case SINT64_T: _map_value(int64_t, value.number.val); + case FLOAT_T: _map_value(float, value.number.val); + case DOUBLE_T: _map_value(double, value.number.val); + case LDOUBLE_T: _map_value(long double, value.number.val); + case STRING_T: _map_value(char*, value.string.val); + case POINTER_T: _map_value(void*, value.data.val); + default: tp_raise(&ffi_type_void, tp_printf(tp, "invalid type \"%c\"")); + } + return result; +} + +#define _unmap_value(cast, mapper) result = mapper(*(cast*)value); break; +tp_obj unmap_value(TP, char type, void* value) { + tp_obj result; + switch(type) { + case UCHAR_T: + case UINT8_T: _unmap_value(uint8_t, tp_number); + case SCHAR_T: + case SINT8_T: _unmap_value(int8_t, tp_number); + case USHORT_T: + case UINT16_T: _unmap_value(uint16_t, tp_number); + case SSHORT_T: + case SINT16_T: _unmap_value(int16_t, tp_number); + case UINT_T: + case UINT32_T: _unmap_value(uint32_t, tp_number); + case SINT_T: + case SINT32_T: _unmap_value(int32_t, tp_number); + case ULONG_T: + case UINT64_T: _unmap_value(uint64_t, tp_number); + case SLONG_T: + case SINT64_T: _unmap_value(int64_t, tp_number); + case FLOAT_T: _unmap_value(float, tp_number); + case DOUBLE_T: _unmap_value(double, tp_number); + case LDOUBLE_T: _unmap_value(long double, tp_number); + case STRING_T: if(*(char**)value == NULL) result = tp_None; else _unmap_value(char*, tp_string); + case POINTER_T: result = tp_data(tp, 0, *(void**)value); break; + default: tp_raise(tp_None, tp_printf(tp, "invalid type \"%c\"")); + } + free(value); + return result; +} + +/* handle = dl.open("library") */ +tp_obj dl_dlopen(TP) { + tp_obj name = TP_STR(); + tp_obj handle = tp_data(tp, 0, NULL); + handle.data.val = dlopen(name.string.val, RTLD_LAZY); + if (handle.data.val == NULL) { + tp_raise(tp_None, tp_printf(tp, "%s", dlerror())); + } + return handle; +} + +/* dl.close(handle) */ +tp_obj dl_dlclose(TP) { + tp_obj handle = TP_TYPE(TP_DATA); + dlclose(handle.data.val); + return tp_None; +} + +/* sym = dl.sym(handle, "name") */ +tp_obj dl_dlsym(TP) { + tp_obj handle = TP_TYPE(TP_DATA); + tp_obj name = TP_STR(); + tp_obj result = tp_data(tp, 0, NULL); + void* sym = dlsym(handle.data.val, name.string.val); + result.data.val = sym; + /*fprintf(stderr, "%s => %p\n", name.string.val, sym);*/ + if(result.data.val == NULL) { + tp_raise(tp_None, tp_printf(tp, "%s", dlerror())); + } + return result; +} + +tp_obj dl_size(TP) { + tp_obj signature = TP_STR(); + int i; + int size = 0; + for(i = 0; i < signature.string.len; i++) { + size += type_size(tp, signature.string.val[i]); + } + return tp_number(size); +} + +void dl_free_data(TP, tp_obj self) { + free(self.data.val); +} + +tp_obj dl_pack(TP) { + tp_obj signature = TP_STR(); + tp_obj values = TP_TYPE(TP_LIST); + int i = 0; + int size = 0; + for(i = 0; i < signature.string.len; i++) { + size += type_size(tp, signature.string.val[i]); + } + void* data = malloc(size); + int offset = 0; + for(i = 0; i < signature.string.len; i++) { + void* arg = map_value(tp, signature.string.val[i], values.list.val->items[i]); + memcpy(data + offset, arg, type_size(tp, signature.string.val[i])); + offset += type_size(tp, signature.string.val[i]); + } + tp_obj result = tp_data(tp, 0, data); + result.data.info->free = dl_free_data; + return result; +} + +tp_obj dl_unpack(TP) { + tp_obj signature = TP_STR(); + tp_obj packed = TP_TYPE(TP_DATA); + int i = 0; + int offset = 0; + tp_obj result = tp_list(tp); + + for(i = 0; i < signature.string.len; i++) { + void* value = map_value(tp, signature.string.val[i], tp_None); + memcpy(value, packed.data.val + offset, type_size(tp, signature.string.val[i])); + tp_params_v(tp, 2, result, unmap_value(tp, signature.string.val[i], value)); + tp_append(tp); + offset += type_size(tp, signature.string.val[i]); + } + return result; +} + +/* result = dl.call(sym, 'return_type', 'signature', [args]) */ +tp_obj dl_call(TP) { + tp_obj method = TP_TYPE(TP_DATA); + tp_obj return_type = TP_STR(); + tp_obj signature = TP_STR(); + tp_obj arguments = TP_TYPE(TP_LIST); + + int num_args = signature.string.len; + ffi_type *args[num_args]; + void* values[num_args]; + int i; + ffi_cif cif; + + for(i = 0; i < num_args; i++) { + args[i] = map_type(tp, signature.string.val[i]); + values[i] = map_value(tp, signature.string.val[i], arguments.list.val->items[i]); + /*printf("%d: %c => %p %p %d\n", i, signature.string.val[i], *(void**)values[i], arguments.list.val->items[i].data.val, (int)arguments.list.val->items[i].number.val);*/ + } + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, num_args, map_type(tp, return_type.string.val[0]), args) == FFI_OK) { + void* result = NULL; + if(return_type.string.val[0] != VOID_T) result = map_value(tp, return_type.string.val[0], tp_None); + /*if(result != NULL) printf("result>%f\n", *(double*) result);*/ + ffi_call(&cif, (void (*)(void))method.data.val, result, values); + /*printf("result<%f\n", *(double*) result);*/ + for(i = 0; i < num_args; i++) free(values[i]); + if(result != NULL) { + return unmap_value(tp, return_type.string.val[0], result); + } + return tp_None; + } + + tp_raise(tp_None, tp_printf(tp, "dl.call: not implemented")); +} + +tp_obj call_method(TP) { + tp_obj self = TP_OBJ(); + tp_obj args = _tp_list_copy(tp, tp->params); + + tp_obj symbol = tp_get(tp, self, tp_string("symbol")); + tp_obj return_type = tp_get(tp, self, tp_string("return_type")); + tp_obj signature = tp_get(tp, self, tp_string("signature")); + + tp_params_v(tp, 4, symbol, return_type, signature, args); + return dl_call(tp); +} + +/* Returns an object which calls the method */ +tp_obj dl_load(TP) { + tp_obj library = TP_STR(); + tp_obj name = TP_STR(); + tp_obj return_type = TP_STR(); + tp_obj signature = TP_STR(); + + tp_params_v(tp, 1, library); + tp_obj handle = dl_dlopen(tp); + + tp_obj output = tp_object(tp); + + tp_params_v(tp, 2, handle, name); + tp_set(tp, output, tp_string("__name__"), name); + tp_set(tp, output, tp_string("symbol"), dl_dlsym(tp)); + tp_set(tp, output, tp_string("return_type"), return_type); + tp_set(tp, output, tp_string("signature"), signature); + tp_set(tp, output, tp_string("__call__"), tp_method(tp, output, call_method)); + + tp_params_v(tp, 1, handle); + dl_dlclose(tp); + return output; +} + +tp_obj dl_exception(TP) { + return tp->ex; +} + +/* + * dl_mod_init() + * + * dl module initialization function + */ +void dl_init(TP) +{ + /* + * module dict for dl + */ + tp_obj mod = tp_dict(tp); + + /* + * bind functions to dl module + */ + tp_set(tp, mod, tp_string("open"), tp_fnc(tp, dl_dlopen)); + tp_set(tp, mod, tp_string("close"), tp_fnc(tp, dl_dlclose)); + tp_set(tp, mod, tp_string("sym"), tp_fnc(tp, dl_dlsym)); + tp_set(tp, mod, tp_string("call"), tp_fnc(tp, dl_call)); + tp_set(tp, mod, tp_string("load"), tp_fnc(tp, dl_load)); + tp_set(tp, mod, tp_string("size"), tp_fnc(tp, dl_size)); /* size of data according to signature */ + tp_set(tp, mod, tp_string("pack"), tp_fnc(tp, dl_pack)); + tp_set(tp, mod, tp_string("unpack"), tp_fnc(tp, dl_unpack)); + tp_set(tp, mod, tp_string("exception"), tp_fnc(tp, dl_exception)); /* get current exception */ + + /* + * bind special attributes to random module + */ + tp_set(tp, mod, tp_string("__doc__"), tp_string("Dynamic library loader.")); + tp_set(tp, mod, tp_string("__name__"), tp_string("dl")); + tp_set(tp, mod, tp_string("__FILE__"), tp_string(__FILE__)); + + /* + * bind random module to tinypy modules[] + */ + tp_set(tp, tp->modules, tp_string("dl"), mod); +} diff --git a/modules/dl/libffi-3.2.1.tar.gz b/modules/dl/libffi-3.2.1.tar.gz new file mode 100644 index 0000000..5c21bb0 Binary files /dev/null and b/modules/dl/libffi-3.2.1.tar.gz differ diff --git a/modules/dl/repl.py b/modules/dl/repl.py new file mode 100644 index 0000000..a3262a9 --- /dev/null +++ b/modules/dl/repl.py @@ -0,0 +1,48 @@ +import dl + +old_str = BUILTINS['str'] + +def repr(value, seen=[]): + if istype(value, "string"): + return "'" + my_str(value, seen).replace("'", "\\'") + "'" + return my_str(value, seen) + +def my_str(value, seen=[]): + if value in seen: + return '...' + seen.append(value) + if istype(value, "list"): + return '[' + ', '.join([repr(v, seen) for v in value]) + ']' + elif istype(value, "dict"): + return '{' + ', '.join([repr(k, seen) + ': ' + repr(value[k], seen) for k in value]) + '}' + return old_str(value) + +def globals(): + return BUILTINS['__globals__'] + +BUILTINS['__globals__'] = {} +BUILTINS['globals'] = globals +BUILTINS['str'] = my_str +BUILTINS['repr'] = repr + +fgets = dl.load('', 'fgets', 'S', 'SI*') +puts = dl.load('', 'printf', 'V', 'S') +handle = dl.open('') +stdin = dl.sym(handle, '_IO_2_1_stdin_') + +data = " " * 1024 + +while True: + puts('> ') + source = fgets(data, len(data), stdin) + if source is None: + break + if source.strip() == '': + continue + try: + result = eval(source, globals()) + print(str(result)) + except: + print(dl.exception()) +print('bye') + diff --git a/modules/dl/tests.py b/modules/dl/tests.py new file mode 100644 index 0000000..242f0f8 --- /dev/null +++ b/modules/dl/tests.py @@ -0,0 +1,17 @@ +import dl + +sqrt = dl.load('libm.so.6', 'sqrt', 'd', 'd') + +for value in [2, 10, 100]: + result = sqrt(value * value) + print('sqrt(' + str(value * value) + ') = ' + str(result)) + if result != value: + raise "unexpected result" + +print(dl.size('III')) + +data = dl.pack('III', [1, 2, 3]) +print(data) +result = dl.unpack('III', data) +for i in result: + print(i) diff --git a/modules/jni/init.c b/modules/jni/init.c new file mode 100644 index 0000000..234e01e --- /dev/null +++ b/modules/jni/init.c @@ -0,0 +1,206 @@ +#include +#include + +#define DATA_CLASS 2 +#define DATA_METHOD 1 + +extern JNIEnv *env; /* this is set elsewhere */ + +typedef struct { + jclass cls; + jmethodID id; + char* name; + char* signature; +} jni_method_t; + +void jni_free_method(TP, tp_obj obj) { + jni_method_t* method = (jni_method_t*) obj.data.val; + //(*env)->DeleteGlobalRef(env, method->id); + free(method->name); + free(method->signature); + free(method); +} + +jvalue jNULL = {.l=NULL}; + +tp_obj jni_find_class(TP) { + tp_obj class_name = TP_STR(); + jclass cls = (*env)->FindClass(env, class_name.string.val); + return tp_data(tp, 0, cls); +} + +tp_obj jni_get_method_id(TP) { + tp_obj cls = TP_TYPE(TP_DATA); + tp_obj name = TP_STR(); + tp_obj signature = TP_STR(); + + jmethodID id = (*env)->GetMethodID(env, cls.data.val, name.string.val, signature.string.val); + if(id == NULL) return tp_None; + + jni_method_t* method = malloc(sizeof(jni_method_t)); + method->cls = cls.data.val; + method->id = id; //(*env)->NewGlobalRef(env, id); + method->name = strdup(name.string.val); + method->signature = strdup(signature.string.val); + //(*env)->DeleteLocalRef(env, id); + + tp_obj result = tp_data(tp, DATA_METHOD, method); + result.data.info->free = jni_free_method; + + return result; +} + +jvalue jni_map_array(TP, const char* type, tp_obj value) { + tp_raise(jNULL, tp_printf(tp, "not implemented")); +} + +tp_obj jni_unmap_array(TP, const char* type, jvalue value) { + tp_raise(tp_None, tp_printf(tp, "not implemented")); +} + +jvalue jni_map_value(TP, const char* type, tp_obj value) { + jvalue result; + switch(*type) { + case 'Z': if(value.type == TP_NUMBER) { result.z = (int) value.number.val; return result; } break; + case 'B': if(value.type == TP_NUMBER) { result.b = (unsigned char) value.number.val; return result; } break; + case 'C': if(value.type == TP_NUMBER) { result.c = (char) value.number.val; return result; } break; + case 'S': if(value.type == TP_NUMBER) { result.s = (short) value.number.val; return result; } break; + case 'I': if(value.type == TP_NUMBER) { result.i = (int) value.number.val; return result; } break; + case 'J': if(value.type == TP_NUMBER) { result.j = (long int) value.number.val; return result; } break; + case 'F': if(value.type == TP_NUMBER) { result.f = (float) value.number.val; return result; } break; + case 'D': if(value.type == TP_NUMBER) { result.d = (double) value.number.val; return result; } break; + case 'L': if(value.type == TP_NONE) { + result.l = NULL; + } else if(!strncmp(type, "Ljava/lang/String;", 18) && value.type == TP_STRING) { + result.l = (*env)->NewStringUTF(env, value.string.val); + return result; + } else if(value.type == TP_DATA) { + result.l = value.data.val; + return result; + } + break; + if(value.type == TP_NONE) { + result.l = NULL; + } else if(value.type == TP_LIST) { + return jni_map_array(tp, type + 1, value); + } + break; + } + tp_raise(jNULL, tp_printf(tp, "unsupported type or type mismatch")); +} + +tp_obj jni_unmap_value(TP, const char* type, jvalue value) { + switch(*type) { + case 'Z': return tp_number(value.z); break; + case 'B': return tp_number(value.b); break; + case 'C': return tp_number(value.c); break; + case 'S': return tp_number(value.s); break; + case 'I': return tp_number(value.i); break; + case 'J': return tp_number(value.j); break; + case 'F': return tp_number(value.f); break; + case 'D': return tp_number(value.d); break; + case 'L': if(value.l == NULL) { + return tp_None; + } else if(!strncmp(type, "Ljava/lang/String;", 18)) { + return tp_string((*env)->GetStringUTFChars(env, value.l, NULL)); + } else { + return tp_data(tp, 0, value.l); + } + break; + case '[': return jni_unmap_array(tp, type + 1, value); + } + tp_raise(tp_None, tp_printf(tp, "unsupported type or type mismatch")); +} + +jvalue* jni_convert_args(TP, const char* signature) { + jvalue* arguments = malloc(sizeof(jvalue) * tp->params.list.val->len); + int i = 0; + signature++; + while(*signature != '\0' && *signature != ')') { + if(*signature == '(') { + continue; + } else if(*signature == ')') { + break; + } else { + tp_obj arg = _tp_list_get(tp,tp->params.list.val, i, "convert_args"); + arguments[i] = jni_map_value(tp, signature, arg); + if(*signature == '[') signature++; + else if(*signature == 'L') { + signature++; + while(*signature != ';') signature++; + } + i++; + } + signature++; + } + return arguments; +} + +tp_obj jni_call_object_method(TP) { + tp_obj object = TP_TYPE(TP_DATA); + tp_obj tp_method = TP_TYPE(TP_DATA); + jni_method_t* method = (jni_method_t*) tp_method.data.val; + + jvalue* arguments = jni_convert_args(tp, method->signature); + const char* return_type = strchr(method->signature, ')') + 1; + + jvalue result; + switch(*return_type) { + case 'Z': result.z = (*env)->CallBooleanMethodA(env, object.data.val, method->id, arguments); break; + case 'B': result.b = (*env)->CallByteMethodA(env, object.data.val, method->id, arguments); break; + case 'C': result.c = (*env)->CallCharMethodA(env, object.data.val, method->id, arguments); break; + case 'S': result.s = (*env)->CallShortMethodA(env, object.data.val, method->id, arguments); break; + case 'I': result.i = (*env)->CallIntMethodA(env, object.data.val, method->id, arguments); break; + case 'J': result.j = (*env)->CallLongMethodA(env, object.data.val, method->id, arguments); break; + case 'F': result.f = (*env)->CallFloatMethodA(env, object.data.val, method->id, arguments); break; + case 'D': result.d = (*env)->CallDoubleMethodA(env, object.data.val, method->id, arguments); break; + case 'L': result.l = (*env)->CallObjectMethodA(env, object.data.val, method->id, arguments); break; + case 'V': (*env)->CallVoidMethodA(env, object.data.val, method->id, arguments); free(arguments); return tp_None; break; + default: tp_raise(tp_None, tp_printf(tp, "unsupported return type")); + } + free(arguments); + return jni_unmap_value(tp, return_type, result); +} + +tp_obj jni_new_object(TP) { + tp_obj cls = TP_TYPE(TP_DATA); + tp_obj tp_constructor = TP_TYPE(TP_DATA); + jni_method_t* constructor = (jni_method_t*) tp_constructor.data.val; + + jvalue* arguments = jni_convert_args(tp, constructor->signature); + jobject object = (*env)->NewObjectA(env, cls.data.val, constructor->id, arguments); + + free(arguments); + return tp_data(tp, 0, object); +} + +/* + * init jni module, namely, set its dictionary + */ +void jni_init(TP) +{ + /* + * new a module dict for jni + */ + tp_obj jni_mod = tp_dict(tp); + + tp_set(tp, jni_mod, tp_string("find_class"), tp_fnc(tp, jni_find_class)); + tp_set(tp, jni_mod, tp_string("get_method_id"), tp_fnc(tp, jni_get_method_id)); + tp_set(tp, jni_mod, tp_string("get_static_method_id"), tp_fnc(tp, jni_get_method_id)); + tp_set(tp, jni_mod, tp_string("call_object_method"), tp_fnc(tp, jni_call_object_method)); + tp_set(tp, jni_mod, tp_string("new_object"), tp_fnc(tp, jni_new_object)); + tp_set(tp, jni_mod, tp_string("find_class"), tp_fnc(tp, jni_find_class)); + + /* + * bind special attributes to jni module + */ + tp_set(tp, jni_mod, tp_string("__doc__"), tp_string("This module gives access to java classes.")); + tp_set(tp, jni_mod, tp_string("__name__"), tp_string("jni")); + tp_set(tp, jni_mod, tp_string("__file__"), tp_string(__FILE__)); + + /* + * bind to tiny modules[] + */ + tp_set(tp, tp->modules, tp_string("jni"), jni_mod); +} + diff --git a/setup.py b/setup.py index d8a3fd2..9b8f625 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ def main(): chksize() if len(sys.argv) < 2: - print HELP + print(HELP) return global TEST,CLEAN,BOOT,DEBUG,VALGRIND,SANDBOX @@ -52,7 +52,7 @@ def main(): build = build_gcc if build == None: - print "couldn't detect OS or incorrect compiler command. defaulting to GCC." + print("couldn't detect OS or incorrect compiler command. defaulting to GCC.") build = build_gcc cmd = sys.argv[1] @@ -68,7 +68,7 @@ def main(): elif cmd == "install": install_cpython() else: - print 'invalid command' + print('invalid command') HELP = """ python setup.py command [options] [modules] @@ -116,6 +116,9 @@ def vars_osx(): if 'pygame' in MODULES: VARS['$FLAGS'] += ' `sdl-config --cflags --libs` ' + if 'dl' in MODULES: + VARS['$FLAGS'] += ' -ldl -lffi ' + def vars_linux(): VARS['$RM'] = 'rm -f' VARS['$VM'] = './vm' @@ -129,6 +132,9 @@ def vars_linux(): if 'pygame' in MODULES: VARS['$FLAGS'] += ' `sdl-config --cflags --libs` ' + if 'dl' in MODULES: + VARS['$FLAGS'] += ' -ldl -I/usr/lib/libffi-3.2.1/include/ -lffi ' + if SANDBOX: VARS['$SYS'] += " -sandbox " VARS['$FLAGS'] += " -DTP_SANDBOX " @@ -149,19 +155,19 @@ def do_cmd(cmd): for k,v in VARS.items(): cmd = cmd.replace(k,v) if '$' in cmd: - print 'vars_error',cmd + print('vars_error',cmd) sys.exit(-1) if VALGRIND and (cmd.startswith("./") or cmd.startswith("../")): cmd = "valgrind " + cmd - print cmd + print(cmd) r = os.system(cmd) if r: - print 'exit_status',r + print('exit_status',r) sys.exit(r) def do_chdir(dest): - print 'cd',dest + print('cd',dest) os.chdir(dest) def build_bc(opt=False): @@ -171,11 +177,11 @@ def build_bc(opt=False): fname = mod+".tpc" data = open(fname,'rb').read() cols = 16 - for n in xrange(0,len(data),cols): - out.append(",".join([str(ord(v)) for v in data[n:n+cols]])+',') + for n in range(0,len(data),cols): + out.append(",".join([str(v) for v in data[n:n+cols]])+',') out.append("""};""") out.append("") - f = open('bc.c','wb') + f = open('bc.c','w') f.write('\n'.join(out)) f.close() @@ -224,8 +230,8 @@ def build_blob(): out.append(line) out.append("#endif") out.append('') - dest = os.path.join(TOPDIR,'build','tinypy.h') - print 'writing %s'%dest + dest = 'tinypy.h' #os.path.join(TOPDIR,'build','tinypy.h') + print('writing %s'%dest) f = open(dest,'w') f.write('\n'.join(out)) f.close() @@ -246,8 +252,8 @@ def build_blob(): if line.find('#include "') != -1: continue out.append(line) out.append('') - dest = os.path.join(TOPDIR,'build','tinypy.c') - print 'writing %s'%dest + dest = 'tinypy.c' #os.path.join(TOPDIR,'build','tinypy.c') + print('writing %s'%dest) f = open(dest,'w') f.write('\n'.join(out)) f.close() @@ -260,7 +266,7 @@ def py2bc(cmd,mod): cmd = cmd.replace('$DEST',dest) do_cmd(cmd) else: - print '#',dest,'is up to date' + print('#',dest,'is up to date') def build_gcc(): mods = CORE[:] @@ -296,12 +302,14 @@ def build_gcc(): do_cmd("gcc $WFLAGS -O3 tpmain.c $FLAGS -lm -o tinypy") do_cmd('$TINYPY tests.py $SYS') if DEBUG: - do_cmd("gcc $WFLAGS -g mymain.c $FLAGS -lm -o ../build/tinypy") + do_cmd("gcc $WFLAGS -g mymain.c $FLAGS -lm -o tinypy") else: - do_cmd("gcc $WFLAGS -O3 mymain.c $FLAGS -lm -o ../build/tinypy") + do_cmd("gcc $WFLAGS -O3 mymain.c $FLAGS -lm -o tinypy") if TEST: - do_cmd(os.path.join('..','build','tinypy')+' tests.py $SYS') - test_mods(os.path.join('..','build','tinypy')+' $TESTS') + #do_cmd(os.path.join('..','build','tinypy')+' tests.py $SYS') + do_cmd('tinypy tests.py $SYS') + #test_mods(os.path.join('..','build','tinypy')+' $TESTS') + test_mods('tinypy $TESTS') do_chdir('..') print("# OK") @@ -386,7 +394,7 @@ def shrink(fname): if len(line.strip()) == 0: continue line = line.rstrip() l1,l2 = len(line),len(line.lstrip()) - line = "\t"*((l1-l2)/4)+line.lstrip() + line = "\t"*int((l1-l2)/4)+line.lstrip() #remove comments if '.c' in fname or '.h' in fname: @@ -424,7 +432,7 @@ def chksize(): f = open(fname,'r'); t1 += len(f.read()); f.close() txt = shrink(fname) t2 += len(txt) - print "#",t1,t2,t2-65536 + print("#",t1,t2,t2-65536) return t2 def build_64k(): @@ -439,11 +447,11 @@ def build_64k(): f = open(dest,'w') f.write(txt) f.close() - print '%s saved to %s'%(src,dest) + print('%s saved to %s'%(src,dest)) def build_cpython(): try: from distutils.core import setup, Extension - except: print "cannot import distutils" + except: print("cannot import distutils") do_chdir(os.path.join(TOPDIR,'cpython')) setup(name = "tinypy", @@ -454,7 +462,7 @@ def build_cpython(): def install_cpython(): try: from distutils.core import setup, Extension - except: print "cannot import distutils" + except: print("cannot import distutils") do_chdir(os.path.join(TOPDIR,'cpython')) setup(name = "tinypy", diff --git a/tinypy/boot.py b/tinypy/boot.py index d4675de..cbfc7bd 100644 --- a/tinypy/boot.py +++ b/tinypy/boot.py @@ -8,6 +8,15 @@ def _boot_init(): ARGV = sys.argv _boot_init() +def join(v): + out = b'' + for el in v: + try: + out += el + except TypeError: + out += el.encode('latin1') + return out + def merge(a,b): if isinstance(a,dict): for k in b: a[k] = b[k] @@ -40,6 +49,12 @@ def load(fname): f.close() return r +def read(fname): + f = open(fname,'r') + r = f.read() + f.close() + return r + def save(fname,v): f = open(fname,'wb') f.write(v) diff --git a/tinypy/builtins.c b/tinypy/builtins.c index 88a69b2..621ccb5 100644 --- a/tinypy/builtins.c +++ b/tinypy/builtins.c @@ -6,11 +6,11 @@ tp_obj tp_print(TP) { int n = 0; tp_obj e; TP_LOOP(e) - if (n) { printf(" "); } + if (n) { tp->echo(" ", -1); } tp_echo(tp,e); n += 1; TP_END; - printf("\n"); + tp->echo("\n", -1); return tp_None; } @@ -118,6 +118,24 @@ tp_obj tp_float(TP) { tp_raise(tp_None,tp_string("(tp_float) TypeError: ?")); } +tp_obj tp_builtins_join(TP) { + tp_obj val = TP_OBJ(); + int l=0,i; + tp_obj r; + char *s; + for (i=0; ilen; i++) { + l += tp_str(tp,val.list.val->items[i]).string.len; + } + r = tp_string_t(tp,l); + s = r.string.info->s; + l = 0; + for (i=0; ilen; i++) { + tp_obj e; + e = tp_str(tp,val.list.val->items[i]); + memcpy(s+l,e.string.val,e.string.len); l += e.string.len; + } + return tp_track(tp,r); +} tp_obj tp_save(TP) { char fname[256]; tp_cstr(tp,TP_STR(),fname,256); diff --git a/tinypy/encode.py b/tinypy/encode.py index de72e1a..156a072 100644 --- a/tinypy/encode.py +++ b/tinypy/encode.py @@ -48,7 +48,7 @@ def setpos(v): text = D.lines[line-1] D.lineno = line val = text + "\0"*(4-len(text)%4) - code_16(POS,len(val)/4,line) + code_16(POS,int(len(val)/4),line) write(val) def code(i,a=0,b=0,c=0): if not istype(i,'number'): raise @@ -659,5 +659,7 @@ def encode(fname,s,t): D.end() map_tags() out = D.out; D = None - return ''.join(out) + # Use a function instead of ''.join() so that bytes and + # strings during bootstrap + return join(out) diff --git a/tinypy/ops.c b/tinypy/ops.c index f294417..a9ecf91 100644 --- a/tinypy/ops.c +++ b/tinypy/ops.c @@ -2,12 +2,91 @@ * Various tinypy operations. */ +tp_obj tp_str_(TP, tp_obj self, tp_obj visited); +tp_obj tp_has(TP,tp_obj self, tp_obj k); + +tp_obj tp_repr_(TP, tp_obj self, tp_obj visited) { + if(self.type == TP_STRING) { + tp_params_v(tp, 3, self, tp_string("'"), tp_string("\\'")); tp_obj replaced = tp_replace(tp); + return tp_printf(tp, "\'%s\'", replaced.string.val); + return self; + } + return tp_str_(tp, self, visited); +} + +tp_obj tp_repr(TP, tp_obj self) { + tp_obj visited = tp_list(tp); + tp_obj result = tp_repr_(tp, self, visited); + return result; +} + /* Function: tp_str * String representation of an object. + * Checks for recursive data structures * * Returns a string object representating self. */ -tp_obj tp_str(TP,tp_obj self) { +tp_obj tp_str_(TP, tp_obj self, tp_obj visited) { + int type = self.type; + if(type == TP_DICT) { + if(tp_has(tp, visited, tp_data(tp, 0, self.dict.val)).number.val) return tp_string("{...}"); + _tp_list_append(tp, visited.list.val, tp_data(tp, 0, self.dict.val)); + } else if(type == TP_LIST) { + if(tp_has(tp, visited, tp_data(tp, 0, self.list.val)).number.val) return tp_string("[...]"); + _tp_list_append(tp, visited.list.val, tp_data(tp, 0, self.list.val)); + } + tp_obj result = tp_None; + if (type == TP_STRING) { + result = self; + } else if (type == TP_NUMBER) { + tp_num v = self.number.val; + if ((fabs(v-(long)v)) < 0.000001) { return tp_printf(tp,"%ld",(long)v); } + result = tp_printf(tp,"%f",v); + } else if(type == TP_DICT) { + result = tp_string("{"); + int i, n = 0; + for(i = 0; i < self.dict.val->alloc; i++) { + if(self.dict.val->items[i].used > 0) { + result = tp_add(tp, result, tp_repr_(tp, self.dict.val->items[i].key, visited)); + result = tp_add(tp, result, tp_string(": ")); + result = tp_add(tp, result, tp_repr_(tp, self.dict.val->items[i].val, visited)); + if(n < self.dict.val->len - 1) result = tp_add(tp, result, tp_string(", ")); + n += 1; + } + } + result = tp_add(tp, result, tp_string("}")); + /*result = tp_printf(tp,"",self.dict.val);*/ + } else if(type == TP_LIST) { + result = tp_string("["); + int i; + for(i = 0; i < self.list.val->len; i++) { + result = tp_add(tp, result, tp_repr_(tp, self.list.val->items[i], visited)); + if(i < self.list.val->len - 1) result = tp_add(tp, result, tp_string(", ")); + } + result = tp_add(tp, result, tp_string("]")); + /*result = tp_printf(tp,"",self.list.val);*/ + } else if (type == TP_NONE) { + result = tp_string("None"); + } else if (type == TP_DATA) { + result = tp_printf(tp,"",self.data.val); + } else if (type == TP_FNC) { + result = tp_printf(tp,"",self.fnc.info); + } else { + result = tp_string(""); + } + if(type == TP_DICT || type == TP_LIST) { + _tp_list_pop(tp, visited.list.val, visited.list.val->len - 1, "visited list is empty"); + } + return result; +} + +tp_obj tp_str(TP, tp_obj self) { + tp_obj visited = tp_list(tp); + tp_obj result = tp_str_(tp, self, visited); + return result; +} + +tp_obj tp_str_old(TP,tp_obj self) { int type = self.type; if (type == TP_STRING) { return self; } if (type == TP_NUMBER) { @@ -28,6 +107,7 @@ tp_obj tp_str(TP,tp_obj self) { return tp_string(""); } + /* Function: tp_bool * Check the truth value of an object * diff --git a/tinypy/py2bc.py b/tinypy/py2bc.py index f7d63af..739d2ec 100644 --- a/tinypy/py2bc.py +++ b/tinypy/py2bc.py @@ -47,16 +47,16 @@ def tinypy(): return import_fname(ARGV[0],'__main__') def main(src,dest): - s = load(src) + s = read(src) r = _compile(s,src) save(dest,r) if __name__ == '__main__': main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) - main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) +# main(ARGV[1],ARGV[2]) diff --git a/tinypy/repl.c b/tinypy/repl.c new file mode 100644 index 0000000..6f6c86c --- /dev/null +++ b/tinypy/repl.c @@ -0,0 +1,50 @@ +#define CPYTHON_MOD + +#include "tp.c" + +#include +#include + +/* Compile with: "gcc -o repl tinypy/repl.c -lm -lreadline -Wall" + Supports basic one-line instructions. No module included. +*/ + +tp_obj run_protected(TP, char* source, tp_obj globals) { + if(setjmp(tp->nextexpr)) { + --(tp->cur); + tp_print_stack(tp); + return tp_None; + } + return tp_eval(tp, source, globals); +} + +int main(int argc, char *argv[]) { + char* line; + + using_history(); + stifle_history(100); + read_history(".tinypy_history"); + + tp_vm *tp = tp_init(argc,argv); + tp_obj globals = tp_dict(tp); + tp->echo("Tinypy REPL.\n", -1); + while(NULL != (line = readline("> "))) { + if(!strcmp(line, "quit")) { + break; + } else if(!strcmp(line, "globals")) { + tp_echo(tp, tp_str(tp, globals)); + tp->echo("\n", -1); + continue; + } + add_history (line); + write_history(".tinypy_history"); + tp_obj result = run_protected(tp, line, globals); + tp_echo(tp, result); + tp->echo("\n", -1); + free(line); + } + tp_deinit(tp); + return(0); +} + +/**/ diff --git a/tinypy/string.c b/tinypy/string.c index 0e7e304..1b99280 100644 --- a/tinypy/string.c +++ b/tinypy/string.c @@ -124,6 +124,13 @@ tp_obj tp_str2(TP) { return tp_str(tp,v); } +tp_obj tp_repr(TP, tp_obj self); + +tp_obj tp_repr2(TP) { + tp_obj v = TP_OBJ(); + return tp_repr(tp,v); +} + tp_obj tp_chr(TP) { int v = TP_NUM(); return tp_string_n(tp->chars[(unsigned char)v],1); diff --git a/tinypy/tests.py b/tinypy/tests.py index 246da38..7f3bf6e 100644 --- a/tinypy/tests.py +++ b/tinypy/tests.py @@ -221,7 +221,9 @@ def showerror(cmd, ss, ex, res): def t_render(ss,ex,exact=True): import tokenize, parse, encode - + if not is_tinypy: + #ss = ss.encode('latin1') + ex = ex.encode('latin1') if not istype(ss,'list'): ss =[ss] n = 1 for s in ss: @@ -1143,7 +1145,8 @@ def t_boot(ss,ex,exact=True): n += 1 system_rm('tmp.txt') #system(TINYPY+fname+' > tmp.txt') - system("../build/tinypy "+fname+' > tmp.txt') + #system("../build/tinypy "+fname+' > tmp.txt') + system("tinypy "+fname+' > tmp.txt') res = load(TMP).strip() print(ss,ex,res) if exact: assert(res == ex) diff --git a/tinypy/tp.h b/tinypy/tp.h index 4021cc9..1764e92 100644 --- a/tinypy/tp.h +++ b/tinypy/tp.h @@ -171,7 +171,7 @@ typedef struct tp_frame_ { int cregs; } tp_frame_; -#define TP_GCMAX 4096 +#define TP_GCMAX 16384 /* FIXME: increased so that gc doesn't get called while running tp_str() */ #define TP_FRAMES 256 #define TP_REGS_EXTRA 2 /* #define TP_REGS_PER_FRAME 256*/ @@ -211,8 +211,10 @@ typedef struct tp_vm { #endif int jmp; tp_obj ex; + tp_obj last_result; char chars[256][2]; int cur; + void (*echo)(const char* data, int length); /* gc */ _tp_list *white; _tp_list *grey; @@ -373,9 +375,11 @@ tp_inline static tp_obj tp_number(tp_num v) { return val; } +tp_obj tp_str_old(TP, tp_obj self); + tp_inline static void tp_echo(TP,tp_obj e) { e = tp_str(tp,e); - fwrite(e.string.val,1,e.string.len,stdout); + tp->echo(e.string.val, e.string.len); } /* Function: tp_string_n diff --git a/tinypy/vm.c b/tinypy/vm.c index 21393e6..322ba67 100644 --- a/tinypy/vm.c +++ b/tinypy/vm.c @@ -1,3 +1,9 @@ +/* write to standard output */ +void tp_default_echo(const char* string, int length) { + if(length < 0) length = strlen(string); + fwrite(string, 1, length, stdout); +} + /* File: VM * Functionality pertaining to the virtual machine. */ @@ -23,6 +29,7 @@ tp_vm *_tp_init(void) { tp->modules = tp_dict(tp); tp->_params = tp_list(tp); for (i=0; i_params,tp_None,tp_list(tp)); } + tp->echo = tp_default_echo; tp_set(tp,tp->root,tp_None,tp->builtins); tp_set(tp,tp->root,tp_None,tp->modules); tp_set(tp,tp->root,tp_None,tp->_regs); @@ -34,6 +41,7 @@ tp_vm *_tp_init(void) { tp_set(tp, sys, tp_string("version"), tp_string("tinypy 1.2+SVN")); tp_set(tp,tp->modules, tp_string("sys"), sys); tp->regs = tp->_regs.list.val->items; + tp->last_result = tp_None; tp_full(tp); return tp; } @@ -89,7 +97,7 @@ void _tp_raise(TP,tp_obj e) { /*char *x = 0; x[0]=0;*/ if (!tp || !tp->jmp) { #ifndef CPYTHON_MOD - printf("\nException:\n"); tp_echo(tp,e); printf("\n"); + tp->echo("\nException:\n", -1); tp_echo(tp,e); tp->echo("\n", -1); exit(-1); #else tp->ex = e; @@ -103,15 +111,15 @@ void _tp_raise(TP,tp_obj e) { void tp_print_stack(TP) { int i; - printf("\n"); + tp->echo("\n", -1); for (i=0; i<=tp->cur; i++) { if (!tp->frames[i].lineno) { continue; } - printf("File \""); tp_echo(tp,tp->frames[i].fname); printf("\", "); - printf("line %d, in ",tp->frames[i].lineno); - tp_echo(tp,tp->frames[i].name); printf("\n "); - tp_echo(tp,tp->frames[i].line); printf("\n"); + tp->echo("File \"", -1); tp_echo(tp,tp->frames[i].fname); tp->echo("\", ", -1); + tp_echo(tp, tp_printf(tp, "line %d, in ",tp->frames[i].lineno)); + tp_echo(tp,tp->frames[i].name); tp->echo("\n ", -1); + tp_echo(tp,tp->frames[i].line); tp->echo("\n", -1); } - printf("\nException:\n"); tp_echo(tp,tp->ex); printf("\n"); + tp->echo("\nException:\n", -1); tp_echo(tp,tp->ex); tp->echo("\n", -1); } void tp_handle(TP) { @@ -242,7 +250,7 @@ int tp_step(TP) { int i; for(i=0;i<16;i++) { fprintf(stderr,"%d: %s\n",i,TP_xSTR(regs[i])); } */ switch (e.i) { - case TP_IEOF: tp_return(tp,tp_None); SR(0); break; + case TP_IEOF: tp->last_result = RA; tp_return(tp,tp_None); SR(0); break; case TP_IADD: RA = tp_add(tp,RB,RC); break; case TP_ISUB: RA = tp_sub(tp,RB,RC); break; case TP_IMUL: RA = tp_mul(tp,RB,RC); break; @@ -335,7 +343,7 @@ int tp_step(TP) { tp_params_v(tp,3,tp_string("DEBUG:"),tp_number(VA),RA); tp_print(tp); break; case TP_INONE: RA = tp_None; break; - case TP_ILINE: + case TP_ILINE: { #ifdef TP_SANDBOX tp_bounds(tp,cur,VA); #endif @@ -345,6 +353,7 @@ int tp_step(TP) { f->line = tp_string_sub(tp,f->code,a,a+VA*4-1); /* fprintf(stderr,"%7d: %s\n",UVBC,f->line.string.val);*/ cur += VA; f->lineno = UVBC; + } break; case TP_IFILE: f->fname = RA; break; case TP_INAME: f->name = RA; break; @@ -451,6 +460,8 @@ tp_obj tp_import_(TP) { return r; } +tp_obj tp_eval_(TP); + void tp_builtins(TP) { tp_obj o; struct {const char *s;void *f;} b[] = { @@ -459,12 +470,12 @@ void tp_builtins(TP) { {"import",tp_import_}, {"len",tp_len_}, {"assert",tp_assert}, {"str",tp_str2}, {"float",tp_float}, {"system",tp_system}, {"istype",tp_istype}, {"chr",tp_chr}, {"save",tp_save}, - {"load",tp_load}, {"fpack",tp_fpack}, {"abs",tp_abs}, - {"int",tp_int}, {"exec",tp_exec_}, {"exists",tp_exists}, + {"load",tp_load}, {"read",tp_load}, {"fpack",tp_fpack}, {"abs",tp_abs}, + {"int",tp_int}, {"eval",tp_eval_}, {"exec",tp_exec_}, {"exists",tp_exists}, {"mtime",tp_mtime}, {"number",tp_float}, {"round",tp_round}, {"ord",tp_ord}, {"merge",tp_merge}, {"getraw",tp_getraw}, {"setmeta",tp_setmeta}, {"getmeta",tp_getmeta}, - {"bool", tp_builtins_bool}, + {"bool", tp_builtins_bool}, {"join", tp_builtins_join}, {"repr", tp_repr2}, #ifdef TP_SANDBOX {"sandbox",tp_sandbox_}, #endif @@ -512,7 +523,18 @@ tp_obj tp_exec(TP, tp_obj code, tp_obj globals) { tp_obj tp_eval(TP, const char *text, tp_obj globals) { tp_obj code = tp_compile(tp,tp_string(text),tp_string("")); - return tp_exec(tp,code,globals); + tp_exec(tp,code,globals); + return tp->last_result; +} + +tp_obj tp_eval_(TP) { + tp_obj text = TP_STR(); + tp_obj globals = TP_TYPE(TP_DICT); + + tp_obj code = tp_compile(tp, text, tp_string("")); + + tp_exec(tp,code,globals); + return tp->last_result; } /* Function: tp_init