diff --git a/HISTORY b/HISTORY index d054e37..f58f86f 100755 --- a/HISTORY +++ b/HISTORY @@ -1,4 +1,5 @@ Jan. 15 2016 +Added a REFLECT statement Added an MB_ENABLE_USERTYPE_REF macro Added an MB_ENABLE_ARRAY_REF macro Fixed some crash bugs with invalid syntax diff --git a/MY-BASIC Quick Reference.pdf b/MY-BASIC Quick Reference.pdf index 0bdb5c3..f11c56c 100644 Binary files a/MY-BASIC Quick Reference.pdf and b/MY-BASIC Quick Reference.pdf differ diff --git a/README.md b/README.md index ec360be..5bfc4a5 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Why are existing script interpreters so complex? Why is it so difficult to integ ## Introduction -MY-BASIC is a lightweight cross-platform easy extendable BASIC interpreter written in pure C with about 14,000 lines of source code. It supports structured grammar, and implements a style of OOP called [prototype-based programming](https://en.wikipedia.org/wiki/Prototype-based_programming) paradigm, furthermore it offers a functional programming ability with [lambda abstraction](https://en.wikipedia.org/wiki/Anonymous_function). Most advanced features are easily cuttable by modifying macros. It is aimed to be either an embeddable scripting language or a standalone interpreter. The core is pretty light; all in a C source file and an associated header file; simpleness of source file layout and tightness dependency make it feels extraordinarily tough. It's able to easily combine MY-BASIC with an existing project in C, C++, Objective-C, C#, etc. Script driven can make your projects more powerful, elegant and neat. It's also able to learn how to build an interpreter from scratch with MY-BASIC, or build your own dialect easily based on it. +MY-BASIC is a lightweight cross-platform easy extendable BASIC interpreter written in pure C with about 15,000 lines of source code. It supports structured grammar, and implements a style of OOP called [prototype-based programming](https://en.wikipedia.org/wiki/Prototype-based_programming) paradigm, furthermore it offers a functional programming ability with [lambda abstraction](https://en.wikipedia.org/wiki/Anonymous_function). Most advanced features are easily cuttable by modifying macros. It is aimed to be either an embeddable scripting language or a standalone interpreter. The core is pretty light; all in a C source file and an associated header file; simpleness of source file layout and tightness dependency make it feels extraordinarily tough. It's able to easily combine MY-BASIC with an existing project in C, C++, Objective-C, C#, etc. Script driven can make your projects more powerful, elegant and neat. It's also able to learn how to build an interpreter from scratch with MY-BASIC, or build your own dialect easily based on it. ## Compatibility diff --git a/core/my_basic.c b/core/my_basic.c index 87db806..9e917e0 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -1418,6 +1418,10 @@ static int _clone_clsss_field(void* data, void* extra, void* n); static bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret); static bool_t _is_class(_class_t* instance, void* m, void* ret); static bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret); +#ifdef MB_ENABLE_COLLECTION_LIB +static int _reflect_class_field(void* data, void* extra, void* d); +#endif /* MB_ENABLE_COLLECTION_LIB */ +static _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg); #endif /* MB_ENABLE_CLASS */ static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f); static void _begin_routine(mb_interpreter_t* s); @@ -1617,6 +1621,7 @@ static int _core_class(mb_interpreter_t* s, void** l); static int _core_endclass(mb_interpreter_t* s, void** l); static int _core_new(mb_interpreter_t* s, void** l); static int _core_var(mb_interpreter_t* s, void** l); +static int _core_reflect(mb_interpreter_t* s, void** l); #endif /* MB_ENABLE_CLASS */ #ifdef MB_ENABLE_LAMBDA static int _core_lambda(mb_interpreter_t* s, void** l); @@ -1734,6 +1739,7 @@ static const _func_t _core_libs[] = { { "ENDCLASS", _core_endclass }, { "NEW", _core_new }, { "VAR", _core_var }, + { "REFLECT", _core_reflect }, #endif /* MB_ENABLE_CLASS */ #ifdef MB_ENABLE_LAMBDA @@ -6166,6 +6172,67 @@ bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret) { return true; } + +#ifdef MB_ENABLE_COLLECTION_LIB +int _reflect_class_field(void* data, void* extra, void* d) { + /* Reflect each field of a class instance to a dictionary */ + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + _var_t* var = 0; + _routine_t* sub = 0; + _dict_t* coll = (_dict_t*)d; + mb_unrefvar(extra); + + mb_assert(data && d); + + obj = (_object_t*)data; + if(_is_internal_object(obj)) + goto _exit; + switch(obj->type) { + case _DT_VAR: + var = (_var_t*)obj->data.variable; + if(!_ht_find(coll->dict, var->name)) { + mb_value_t kv, vv; + mb_make_string(kv, var->name); + _internal_object_to_public_value(obj, &vv); + _set_dict(coll, &kv, &vv, 0, 0); + } + + break; + case _DT_ROUTINE: + sub = (_routine_t*)obj->data.routine; + if(!_ht_find(coll->dict, sub->name)) { + mb_value_t kv, vv; + mb_make_string(kv, sub->name); + mb_make_type(vv, _internal_type_to_public_type(obj->type)); + _set_dict(coll, &kv, &vv, 0, 0); + } + + break; + default: /* Do nothing */ + break; + } + +_exit: + return result; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg) { + /* Reflect a class instance from a string */ + _ls_node_t* cs = 0; + _object_t* c = 0; + + cs = _search_identifier_in_scope_chain(s, 0, n, 0, 0, 0); + if(!cs || !cs->data) return 0; + c = (_object_t*)cs->data; + if(!c) return 0; + c = _GET_CLASS(c); + if(!c) return 0; + if(arg) _internal_object_to_public_value(c, arg); + + return c->data.instance; +} #endif /* MB_ENABLE_CLASS */ void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f) { @@ -7104,7 +7171,7 @@ int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt) { tgt->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); _init_class(s, tgt->data.instance, mb_strdup(obj->data.instance->name, 0)); _push_scope_by_class(s, tgt->data.instance->scope); - _traverse_class(obj->data.instance, _clone_clsss_field, _clone_class_meta_link, 1, false, tgt->data.instance, 0); + _traverse_class(obj->data.instance, _clone_clsss_field, _clone_class_meta_link, _META_LIST_MAX_DEPTH, false, tgt->data.instance, 0); _pop_scope(s, false); break; @@ -12116,8 +12183,7 @@ int _core_new(mb_interpreter_t* s, void** l) { _object_t obj; _object_t tgt; mb_value_t ret; - _ls_node_t* cs = 0; - _object_t* c = 0; + _class_t* instance = 0; mb_assert(s && l); @@ -12133,14 +12199,9 @@ int _core_new(mb_interpreter_t* s, void** l) { switch(arg.type) { case MB_DT_STRING: arg.value.string = mb_strupr(arg.value.string); - cs = _search_identifier_in_scope_chain(s, 0, arg.value.string, 0, 0, 0); - if(!cs || !cs->data) goto _default; - c = (_object_t*)cs->data; - if(!c) goto _default; - c = _GET_CLASS(c); - if(!c) goto _default; - _internal_object_to_public_value(c, &arg); - _ref(&c->data.instance->ref, c->data.instance); + if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) + goto _default; + _ref(&instance->ref, instance); /* Fall through */ case MB_DT_CLASS: _public_value_to_internal_object(&arg, &obj); @@ -12183,6 +12244,63 @@ _exit: return result; } + +int _core_reflect(mb_interpreter_t* s, void** l) { + /* REFLECT statement */ +#ifdef MB_ENABLE_COLLECTION_LIB + int result = MB_FUNC_OK; + mb_value_t arg; + _object_t obj; + mb_value_t ret; + _class_t* instance = 0; + _dict_t* coll = 0; + + mb_assert(s && l); + + mb_make_nil(ret); + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_func_end(s, l)); + + _MAKE_NIL(&obj); + switch(arg.type) { + case MB_DT_STRING: + arg.value.string = mb_strupr(arg.value.string); + if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) + goto _default; + _ref(&instance->ref, instance); + /* Fall through */ + case MB_DT_CLASS: + _public_value_to_internal_object(&arg, &obj); + coll = _create_dict(s); + _traverse_class(obj.data.instance, _reflect_class_field, 0, _META_LIST_MAX_DEPTH, false, coll, 0); + ret.type = MB_DT_DICT; + ret.value.dict = coll; + + break; + default: +_default: + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, 0, TON(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + _assign_public_value(&arg, 0); + + return result; +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(s); + mb_unrefvar(l); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_COLLECTION_LIB */ +} #endif /* MB_ENABLE_CLASS */ #ifdef MB_ENABLE_LAMBDA @@ -13763,6 +13881,7 @@ int _coll_get(mb_interpreter_t* s, void** l) { case MB_DT_CLASS: _public_value_to_internal_object(&coi, &ocoi); mb_check(mb_pop_string(s, l, &field)); + field = mb_strupr(field); fnode = _search_identifier_in_class(s, ocoi.data.instance, field, 0, 0); if(fnode && fnode->data) { fobj = (_object_t*)fnode->data; diff --git a/core/my_basic.h b/core/my_basic.h index f2fb0f5..54201a6 100755 --- a/core/my_basic.h +++ b/core/my_basic.h @@ -191,6 +191,9 @@ extern "C" { #ifndef mb_make_nil # define mb_make_nil(__v) do { (__v).value.integer = 0; (__v).type = MB_DT_NIL; } while(0) #endif /* mb_make_nil */ +#ifndef mb_make_type +# define mb_make_type(__v, __d) do { (__v).value.type = (__d); (__v).type = MB_DT_TYPE; } while(0) +#endif /* mb_make_type */ #ifndef mb_make_int # define mb_make_int(__v, __d) do { (__v).value.integer = (__d); (__v).type = MB_DT_INT; } while(0) #endif /* mb_make_int */ @@ -203,9 +206,11 @@ extern "C" { #ifndef mb_make_usertype # define mb_make_usertype(__v, __d) do { (__v).value.usertype = (__d); (__v).type = MB_DT_USERTYPE; } while(0) #endif /* mb_make_usertype */ -#ifndef mb_make_usertype_ref -# define mb_make_usertype_ref(__v, __d) do { (__v).value.usertype_ref = (__d); (__v).type = MB_DT_USERTYPE_REF; } while(0) -#endif /* mb_make_usertype_ref */ +#ifdef MB_ENABLE_USERTYPE_REF +# ifndef mb_make_usertype_ref +# define mb_make_usertype_ref(__v, __d) do { (__v).value.usertype_ref = (__d); (__v).type = MB_DT_USERTYPE_REF; } while(0) +# endif /* mb_make_usertype_ref */ +#endif /* MB_ENABLE_USERTYPE_REF */ #ifndef mb_make_array # define mb_make_array(__v, __d) do { (__v).value.array = (__d); (__v).type = MB_DT_ARRAY; } while(0) #endif /* mb_make_array */ @@ -219,7 +224,7 @@ extern "C" { #endif /* MB_ENABLE_COLLECTION_LIB */ #ifndef mb_int_val -# define mb_int_val(__v, __i) do { if((__v).type == MB_DT_INT) (__i) = (__v).value.integer; else if((__v).type == MB_DT_REAL) (__i) = (int_t)((__v).value.float_point); else (__i) = ~((int_t)0); } while(0) +# define mb_int_val(__v, __d) do { if((__v).type == MB_DT_INT) (__d) = (__v).value.integer; else if((__v).type == MB_DT_REAL) (__d) = (int_t)((__v).value.float_point); else (__d) = ~((int_t)0); } while(0) #endif /* mb_int_val */ #ifndef MB_CODES diff --git a/output/my_basic.exe b/output/my_basic.exe index 09788d6..a25c20e 100755 Binary files a/output/my_basic.exe and b/output/my_basic.exe differ