diff --git a/HISTORY b/HISTORY index 3db2860..8ae0a3a 100755 --- a/HISTORY +++ b/HISTORY @@ -1,7 +1,14 @@ +Sep. 11 2015 +Added a duplicate sub routine error handling +Added optional argument support for INPUT statement +Fixed a variable multiple freeing bug in sub routine +Polished document + Sep. 8 2015 Fixed a type parsing of sub routine bug Fixed a scope processing bug Fixed a sub routine invoking bug without leading CALL statement or result receiver +Added a makefile Sep. 6 2015 Improved sub routine @@ -12,7 +19,7 @@ Prompted more friendly dummy function message Polished code and document Sep. 1 2015 -Added support for user customized routine by DEF/ENDDEF +Added support for user customized sub routine by DEF/ENDDEF Aug. 26 2015 Added a memory pool to interpreter shell in main.c @@ -56,7 +63,7 @@ Changed mb_dispose_value as public Apr. 23 2015 Version 1.1 Added debug APIs -Added (nestable) multi line IF statement support +Added (nestable) multiple line IF statement support Apr. 15 2015 Added mb_pop_usertype, mb_push_usertype to support user defined type @@ -102,12 +109,12 @@ Added an mb_remove_reserved_func function which allows a user to disable/remove May. 22 2014 Fixed a crash bug when missing colon in a combined line, thanks to Michael P. Welch for pointing it out -Fixed a missing lexical cursor stepping bug in INPUT function +Fixed a missing lexical cursor stepping bug in INPUT statement Mar. 17 2014 Added an XCode project Added a safe stdin reader function mb_gets -Fixed a crash bug in INPUT function +Fixed a crash bug in INPUT statement Feb. 17 2014 Added mod by zero processing @@ -119,7 +126,7 @@ Jul. 19 2013 Fixed a crash bug in _execute_statement, thanks to Jon Mayo for pointing it out Feb. 25 2013 -Fixed a cross routine multi-statement execution bug, thanks to Bruce Kendall for pointing it out +Fixed a cross routine multiple statement execution bug, thanks to Bruce Kendall for pointing it out Fixed a memory corruption bug when loading a script file Jan. 9 2013 diff --git a/README.md b/README.md index 8e1db24..e272b74 100755 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ ![](resource/icon.ico) -Copyright (C) 2011 - 2015 [Wang Renxin](https://twitter.com/wangrenxin) +Copyright (C) 2011 - 2015 [Wang Renxin](https://twitter.com/wangrenxin). All rights reserved. [LinkedIn](https://cn.linkedin.com/pub/wang-renxin/43/494/20) -Why are existing script interpreters so complex? Why is it so difficult to integrate with them? Why not try MY-BASIC today! +Why are existing script interpreters so complex? Why is it so difficult to integrate with them? Why not try MY-BASIC today! Scripting should be simple and enjoyable. ## Introduction -MY-BASIC is a tiny cross-platform easy extendable BASIC interpreter written in pure C with about 7000 lines of source code. Its grammar is similar to structured BASIC. 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. You can combine MY-BASIC with an existing C, C++, Objective-C, etc. project easily, scripting driven can make your projects more powerful, elegant and neat. +MY-BASIC is a tiny cross-platform easy extendable BASIC interpreter written in pure C with about 7000 lines of source code. Its grammar is similar to structured BASIC. 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. You are able to easily combine MY-BASIC with an existing project in C, C++, Objective-C, etc. Scripting driven can make your projects more powerful, elegant and neat. ## Main features -* It is free +* It is totally free to use MY-BASIC for individual or commercial purpose under the MIT license * Written in clean ANSI C, source code portable * With most common BASIC syntax * Small (within memory usage less than 128KB) and fast @@ -26,11 +26,23 @@ MY-BASIC is a tiny cross-platform easy extendable BASIC interpreter written in p * Standard string functions * Debug APIs * High expansibility, easy to use APIs, easy to write customized scripting interfaces -* It's able to use it as a standalone interpreter, or integrate it with existing C, C++, Objective-C, etc. projects +* It's able to use it as a standalone interpreter, or integrate it with existing projects in C, C++, Objective-C, etc. * It's able to learn how to build an interpreter from scratch from MY-BASIC * It's able to build your own dialect based on MY-BASIC * More features/modules under developing +## Scripting at a glance + +Come along with a traditional "hello world" script in MY-BASIC: + + def foo(a, b) + print a + " " + b; + enddef + + foo("Hello", "world!") + + input + ## [Interpreter workflow diagram](https://github.com/paladin-t/my_basic/wiki/Interpreter-workflow-diagram) ![](https://github.com/paladin-t/my_basic/blob/master/interpreter%20workflow%20diagram.png) @@ -57,7 +69,7 @@ You can definitely [link with MY-BASIC as a lib](https://github.com/paladin-t/my For more details about using MY-BASIC when it's already integrated with exist projects, please see [MY-BASIC Quick Reference](MY-BASIC%20Quick%20Reference.pdf). -## [WIKI](https://github.com/paladin-t/my_basic/wiki) +## [Wiki](https://github.com/paladin-t/my_basic/wiki) * [Passes](https://github.com/paladin-t/my_basic/wiki/Passes) * [Interpreter workflow diagram](https://github.com/paladin-t/my_basic/wiki/Interpreter-workflow-diagram) diff --git a/core/my_basic.c b/core/my_basic.c index 2984049..b4cae88 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -52,6 +52,7 @@ extern "C" { # pragma warning(push) # pragma warning(disable : 4127) # pragma warning(disable : 4305) +# pragma warning(disable : 4996) #endif /* _MSC_VER */ #ifdef __APPLE__ @@ -78,7 +79,7 @@ extern "C" { /** Macros */ #define _VER_MAJOR 1 #define _VER_MINOR 1 -#define _VER_REVISION 68 +#define _VER_REVISION 69 #define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) /* Uncomment the line below to treat warning as error */ @@ -211,6 +212,7 @@ static const char* _ERR_DESC[] = { "Don't suspend in a routine", "Don't mix instructional and structured sub routines", "Routine expected", + "Duplicate routine", /** Extended abort */ "Extended abort" }; @@ -365,6 +367,7 @@ typedef struct _parsing_context_t { _object_t* last_symbol; _parsing_state_e parsing_state; _symbol_state_e symbol_state; + unsigned short class_state; unsigned short routine_state; unsigned short routine_params_state; int parsing_pos; @@ -776,7 +779,9 @@ static bool_t _is_array(void* obj); static bool_t _is_string(void* obj); static char* _extract_string(_object_t* obj); -static void _init_instance(mb_interpreter_t* s, _class_t* instance, char* n); +static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n); +static void _begin_class(mb_interpreter_t* s); +static void _end_class(mb_interpreter_t* s); static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n); static void _begin_routine(mb_interpreter_t* s); static void _end_routine(mb_interpreter_t* s); @@ -843,11 +848,21 @@ static int _close_std_lib(mb_interpreter_t* s); #ifdef _MSC_VER # if _MSC_VER < 1300 -# define _do_nothing(__s, __l, __exit, __result) do { static int i = 0; ++i; printf("Unaccessable function called %d times\n", i); mb_unrefvar(__s); mb_unrefvar(__l); mb_unrefvar(__result); goto _exit; } while(0) +# define _do_nothing(__s, __l, __exit, __result) \ + do { \ + _ls_node_t* ast = 0; static int i = 0; ++i; \ + printf("Unaccessable function called %d times\n", i); \ + ast = (_ls_node_t*)(*(__l)); \ + _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, 0, DON(ast), MB_FUNC_ERR, __exit, __result); \ + } while(0) # endif /* _MSC_VER < 1300 */ #endif /* _MSC_VER */ #ifndef _do_nothing -# define _do_nothing(__s, __l, __exit, __result) do { _ls_node_t* ast = (_ls_node_t*)(*(__l)); _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (char*)MB_FUNC, DON(ast), MB_FUNC_ERR, __exit, __result); } while(0); +# define _do_nothing(__s, __l, __exit, __result) \ + do { \ + _ls_node_t* ast = (_ls_node_t*)(*(__l)); \ + _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (char*)MB_FUNC, DON(ast), MB_FUNC_ERR, __exit, __result); \ + } while(0); #endif /* _do_nothing */ /** Core lib */ @@ -1971,6 +1986,7 @@ int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, _routine_t* r) { _pop_scope(s); result = _public_value_to_internal_object(&arg, var->data); + var->data->ref = true; if(result != MB_FUNC_OK) goto _exit; } @@ -2302,9 +2318,15 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob (*obj)->data.instance = ((_object_t*)(glbsyminscope->data))->data.instance; (*obj)->ref = true; *delsym = true; + if(running != (*obj)->data.instance->scope && + context->class_state && + _IS_FUNC(context->last_symbol, _core_class)) { + _push_scope(s, (*obj)->data.instance->scope); + } } else { tmp.instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_instance(s, tmp.instance, sym); + _init_class(s, tmp.instance, sym); + _push_scope(s, tmp.instance->scope); (*obj)->data.instance = tmp.instance; ul = _ht_set_or_insert(running->var_dict, sym, *obj); @@ -2492,23 +2514,43 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) { goto _exit; } /* _class_t */ - if(context->last_symbol && _IS_FUNC(context->last_symbol, _core_class)) { + if(context->last_symbol) { glbsyminscope = _search_var_in_scope_chain(s, 0, sym); - if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_VAR) { - tmp.obj = (_object_t*)(glbsyminscope->data); - if(!tmp.obj->ref) { - _ht_remove(running->var_dict, sym, _ls_cmp_extra_string); - _dispose_object(tmp.obj); - } - tmp.obj->type = _DT_CLASS; - tmp.obj->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_instance(s, tmp.obj->data.instance, sym); - _ht_set_or_insert(running->var_dict, sym, tmp.obj); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { + if(_IS_FUNC(context->last_symbol, _core_class)) + _begin_class(s); + result = _DT_CLASS; + + goto _exit; } + if(_IS_FUNC(context->last_symbol, _core_class)) { + if(_IS_FUNC(context->last_symbol, _core_class)) + _begin_class(s); + if(!_is_identifier_char(sym[0])) { + result = _DT_NIL; - result = _DT_CLASS; + goto _exit; + } + if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_VAR) { + tmp.obj = (_object_t*)(glbsyminscope->data); + if(!tmp.obj->ref) { + _ht_remove(running->var_dict, sym, _ls_cmp_extra_string); + _dispose_object(tmp.obj); + } + tmp.obj->type = _DT_CLASS; + tmp.obj->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); + _init_class(s, tmp.obj->data.instance, sym); + _init_class(s, tmp.obj->data.instance, sym); + _ht_set_or_insert(running->var_dict, sym, tmp.obj); + } - goto _exit; + result = _DT_CLASS; + + goto _exit; + } else if(_IS_FUNC(context->last_symbol, _core_endclass)) { + _end_class(s); + _pop_scope(s); + } } /* _routine_t */ if(context->last_symbol) { @@ -3108,14 +3150,39 @@ char* _extract_string(_object_t* obj) { return result; } -void _init_instance(mb_interpreter_t* s, _class_t* instance, char* n) { +void _init_class(mb_interpreter_t* s, _class_t* instance, char* n) { /* Initialize an instance */ + _running_context_t* running = 0; + mb_assert(s && instance && n); + running = s->running_context; + memset(instance, 0, sizeof(_class_t)); instance->name = n; instance->scope = (_running_context_t*)mb_malloc(sizeof(_running_context_t)); memset(instance->scope, 0, sizeof(_running_context_t)); + instance->scope->var_dict = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); +} + +void _begin_class(mb_interpreter_t* s) { + /* Begin parsing a class */ + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + context->class_state++; +} + +void _end_class(mb_interpreter_t* s) { + /* End parsing a class */ + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + context->class_state--; } void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n) { @@ -3279,6 +3346,16 @@ int _dispose_object(_object_t* obj) { safe_free(obj->data.label); } + break; + case _DT_CLASS: + if(!obj->ref) { + safe_free(obj->data.instance->name); + _ht_foreach(obj->data.instance->scope->var_dict, _destroy_object); + _ht_destroy(obj->data.instance->scope->var_dict); + safe_free(obj->data.instance->scope); + safe_free(obj->data.instance); + } + break; case _DT_ROUTINE: if(!obj->ref) { @@ -3291,13 +3368,6 @@ int _dispose_object(_object_t* obj) { safe_free(obj->data.routine); } - break; - case _DT_CLASS: - if(!obj->ref) { - safe_free(obj->data.instance->scope); - safe_free(obj->data.instance); - } - break; case _DT_NIL: /* Fall through */ case _DT_ANY: /* Fall through */ @@ -3599,6 +3669,10 @@ int _execute_statement(mb_interpreter_t* s, _ls_node_t** l) { case _DT_STRING: _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, 0, DON(ast), MB_FUNC_ERR, _exit, result); + break; + case _DT_CLASS: + mb_assert(0 && "Not implemented"); + break; case _DT_ROUTINE: ast = ast->prev; @@ -6161,6 +6235,7 @@ int _core_def(mb_interpreter_t* s, void** l) { /* DEF statement */ int result = MB_FUNC_OK; _ls_node_t* ast = 0; + _running_context_t* running = 0; _object_t* obj = 0; _var_t* var = 0; _ls_node_t* rnode = 0; @@ -6168,6 +6243,8 @@ int _core_def(mb_interpreter_t* s, void** l) { mb_assert(s && l); + running = s->running_context; + ast = (_ls_node_t*)(*l); ast = ast->next; @@ -6177,6 +6254,9 @@ int _core_def(mb_interpreter_t* s, void** l) { if(!_IS_ROUTINE(obj)) { _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, 0, DON(ast), MB_FUNC_ERR, _exit, result); } + if(obj->data.routine->entry) { + _handle_error_on_obj(s, SE_RN_DUPLICATE_ROUTINE, 0, DON(ast), MB_FUNC_ERR, _exit, result); + } routine = (_routine_t*)(((_object_t*)(ast->data))->data.routine); ast = ast->next; obj = (_object_t*)(ast->data); @@ -6233,17 +6313,32 @@ _exit: } int _core_class(mb_interpreter_t* s, void** l) { - /* OBJ statement */ + /* CLASS statement */ int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; - _do_nothing(s, l, _exit, result); + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)(*l); + ast = ast->next; + + _using_jump_set_of_structured(s, ast, _exit, result); + + _skip_to(s, &ast, _core_endclass, _DT_NIL); + + ast = ast->next; _exit: + *l = ast; + return result; } int _core_endclass(mb_interpreter_t* s, void** l) { - /* ENDOBJ statement */ + /* ENDCLASS statement */ int result = MB_FUNC_OK; _do_nothing(s, l, _exit, result); @@ -7132,7 +7227,12 @@ int _std_input(mb_interpreter_t* s, void** l) { ast = (_ls_node_t*)(*l); obj = (_object_t*)(ast->data); - if(!obj || obj->type != _DT_VAR) { + if(!obj || obj->type == _DT_EOS) { + _get_inputer(s)(line, sizeof(line)); + + goto _exit; + } + if(obj->type != _DT_VAR) { _handle_error_on_obj(s, SE_RN_VARIABLE_EXPECTED, 0, DON(ast), MB_FUNC_ERR, _exit, result); } if(obj->data.variable->data->type == _DT_INT || obj->data.variable->data->type == _DT_REAL) { diff --git a/core/my_basic.h b/core/my_basic.h index 83b50ca..8bc9549 100755 --- a/core/my_basic.h +++ b/core/my_basic.h @@ -219,6 +219,7 @@ typedef enum mb_error_e { SE_RN_DONT_SUSPEND_IN_A_ROUTINE, SE_RN_DONT_MIX_INSTRUCTIONAL_AND_STRUCTURED, SE_RN_ROUTINE_EXPECTED, + SE_RN_DUPLICATE_ROUTINE, /** Extended abort */ SE_EA_EXTENDED_ABORT, /** Extra */ diff --git a/makefile b/makefile old mode 100644 new mode 100755 index 72b54e3..5c8d114 --- a/makefile +++ b/makefile @@ -5,7 +5,7 @@ main.o : shell/main.c core/my_basic.h cc -Os -c shell/main.c my_basic.o : core/my_basic.c core/my_basic.h - cc -Os -c core/my_basic.c + cc -Os -c core/my_basic.c -Wno-multichar -Wno-overflow clean : - rm edit $(objects) + rm -f main.o my_basic.o output/my_basic_bin output/my_basic_bin.exe diff --git a/output/my_basic.exe b/output/my_basic.exe index d83a84f..7f62e4a 100755 Binary files a/output/my_basic.exe and b/output/my_basic.exe differ diff --git a/output/my_basic_mac b/output/my_basic_mac index 90f190c..6f29fce 100755 Binary files a/output/my_basic_mac and b/output/my_basic_mac differ diff --git a/resource/my_basic.rc b/resource/my_basic.rc index 47c9678..01605c4 100755 --- a/resource/my_basic.rc +++ b/resource/my_basic.rc @@ -36,8 +36,8 @@ IDI_ICON_MAIN ICON "icon.ico" VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,1,68,0 - PRODUCTVERSION 1,1,68,0 + FILEVERSION 1,1,69,0 + PRODUCTVERSION 1,1,69,0 FILEFLAGSMASK 0x17L # ifdef _DEBUG FILEFLAGS 0x1L @@ -55,13 +55,13 @@ VALUE "Comments", "MY-BASIC" VALUE "CompanyName", "Wang Renxin" VALUE "FileDescription", "MY-BASIC Interpreter for Windows" - VALUE "FileVersion", "1, 1, 68, 0" + VALUE "FileVersion", "1, 1, 69, 0" VALUE "InternalName", "my_basic" VALUE "LegalCopyright", "Copyright (C) 2011 - 2015 Wang Renxin" VALUE "LegalTrademarks", "MY-BASIC" VALUE "OriginalFilename", "my_basic.exe" VALUE "ProductName", "MY-BASIC" - VALUE "ProductVersion", "1, 1, 68, 0" + VALUE "ProductVersion", "1, 1, 69, 0" END END BLOCK "VarFileInfo"