+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.

This commit is contained in:
tony 2015-09-11 22:29:16 +08:00
parent 3b7a983871
commit c6d7dcae8f
8 changed files with 167 additions and 47 deletions

17
HISTORY
View File

@ -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

View File

@ -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)

View File

@ -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,8 +2514,23 @@ _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_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;
goto _exit;
}
if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_VAR) {
tmp.obj = (_object_t*)(glbsyminscope->data);
if(!tmp.obj->ref) {
@ -2502,13 +2539,18 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) {
}
tmp.obj->type = _DT_CLASS;
tmp.obj->data.instance = (_class_t*)mb_malloc(sizeof(_class_t));
_init_instance(s, tmp.obj->data.instance, sym);
_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);
}
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) {

View File

@ -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 */

4
makefile Normal file → Executable file
View File

@ -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

Binary file not shown.

Binary file not shown.

View File

@ -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"