diff --git a/HISTORY b/HISTORY index d355df2..10052bd 100755 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,9 @@ +Apr. 10 2015 +Improved compatibility with PellesC +Fixed a double precision float parsing bug on all 32bit systems, thanks to Pito for pointing it out +Fixed a exponential number parsing bug, thanks to Pito for pointing it out +Fixed a crash bug when a script begins with a meaningless negtive number + Mar. 25 2015 Changed _strupr macro to mb_strupr function Added an mb_strdup function diff --git a/core/my_basic.c b/core/my_basic.c index 82c16f4..c71d025 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -78,7 +78,7 @@ extern "C" { /** Macros */ #define _VER_MAJOR 1 #define _VER_MINOR 0 -#define _VER_REVISION 46 +#define _VER_REVISION 47 #define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) /* Uncomment this line to treat warnings as error */ @@ -258,6 +258,8 @@ typedef struct _label_t { _ls_node_t* node; } _label_t; +typedef unsigned char _raw_t[sizeof(union { int_t i; real_t r; void* p; })]; + typedef struct _object_t { _data_e type; union { @@ -270,6 +272,7 @@ typedef struct _object_t { _array_t* array; _label_t* label; char separator; + _raw_t raw; } data; bool_t ref; int source_pos; @@ -594,6 +597,8 @@ static void* mb_malloc(size_t s); static void* mb_realloc(void** p, size_t s); static void mb_free(void* p); +static size_t mb_memtest(void*p, size_t s); + static char* mb_strupr(char* s); #define safe_free(__p) do { if(__p) { mb_free(__p); __p = 0; } else { mb_assert(0 && "Memory already released"); } } while(0) @@ -656,7 +661,7 @@ static int _append_char_to_symbol(mb_interpreter_t* s, char c); static int _cut_symbol(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col); static int _append_symbol(mb_interpreter_t* s, char* sym, bool_t* delsym, int pos, unsigned short row, unsigned short col); static int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** obj, _ls_node_t*** asgn, bool_t* delsym); -static _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value); +static _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value); static int _parse_char(mb_interpreter_t* s, char c, int pos, unsigned short row, unsigned short col); static void _set_error_pos(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col); @@ -722,6 +727,8 @@ MBAPI int mb_dispose_value(mb_interpreter_t* s, mb_value_t val); # endif /* _MSC_VER < 1300 */ #elif defined __BORLANDC__ # define _do_nothing do { printf("Unaccessable function: %s\n", __FUNC__); } while(0) +#elif defined __POCC__ +# define _do_nothing do { printf("Unaccessable function: %s\n", __func__); } while(0) #else /* _MSC_VER */ # define _do_nothing do { printf("Unaccessable function: %s\n", __FUNCTION__); } while(0) #endif /* _MSC_VER */ @@ -1545,6 +1552,16 @@ void mb_free(void* p) { free(p); } +size_t mb_memtest(void*p, size_t s) { + size_t result = 0; + size_t i = 0; + for(i = 0; i < s; i++) { + result += ((unsigned char*)p)[i]; + } + + return result; +} + char* mb_strupr(char* s) { char* t = s; @@ -2165,8 +2182,8 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob /* Create a syntax symbol */ int result = MB_FUNC_OK; _data_e type; - union { _func_t* func; _array_t* array; _var_t* var; _label_t* label; real_t float_point; int_t integer; void* any; } tmp; - void* value = 0; + union { _func_t* func; _array_t* array; _var_t* var; _label_t* label; real_t float_point; int_t integer; _raw_t any; } tmp; + _raw_t value; unsigned int ul = 0; _parsing_context_t* context = 0; _ls_node_t* glbsyminscope = 0; @@ -2174,6 +2191,8 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob mb_assert(s && sym && obj); + memset(value, 0, sizeof(_raw_t)); + context = (_parsing_context_t*)s->parsing_context; *obj = (_object_t*)mb_malloc(sizeof(_object_t)); @@ -2185,13 +2204,13 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob (*obj)->type = type; switch(type) { case _DT_INT: - tmp.any = value; + memcpy(tmp.any, value, sizeof(_raw_t)); (*obj)->data.integer = tmp.integer; safe_free(sym); break; case _DT_REAL: - tmp.any = value; + memcpy(tmp.any, value, sizeof(_raw_t)); (*obj)->data.float_point = tmp.float_point; safe_free(sym); @@ -2209,7 +2228,7 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob tmp.func = (_func_t*)mb_malloc(sizeof(_func_t)); memset(tmp.func, 0, sizeof(_func_t)); tmp.func->name = sym; - tmp.func->pointer = (mb_func_t)(intptr_t)value; + memcpy(&tmp.func->pointer, value, sizeof(tmp.func->pointer)); (*obj)->data.func = tmp.func; break; @@ -2223,7 +2242,7 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob tmp.array = (_array_t*)mb_malloc(sizeof(_array_t)); memset(tmp.array, 0, sizeof(_array_t)); tmp.array->name = sym; - tmp.array->type = (_data_e)(int)(long)(intptr_t)value; + memcpy(&tmp.array->type, value, sizeof(tmp.array->type)); (*obj)->data.array = tmp.array; ul = _ht_set_or_insert((_ht_node_t*)s->global_var_dict, sym, *obj); @@ -2266,8 +2285,8 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob break; case _DT_LABEL: if(context->current_char == ':') { - if(value) { - (*obj)->data.label = value; + if(mb_memtest(value, sizeof(_raw_t))) { + memcpy(&((*obj)->data.label), value, sizeof((*obj)->data.label)); (*obj)->ref = true; *delsym = true; } else { @@ -2309,15 +2328,17 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob return result; } -_data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { +_data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) { /* Get the type of a syntax symbol */ _data_e result = _DT_NIL; - union { real_t float_point; int_t integer; _object_t* obj; void* any; } tmp; + union { real_t float_point; int_t integer; _object_t* obj; _raw_t any; } tmp; char* conv_suc = 0; _parsing_context_t* context = 0; _ls_node_t* lclsyminscope = 0; _ls_node_t* glbsyminscope = 0; size_t _sl = 0; + _data_e en = _DT_ANY; + intptr_t ptr = 0; mb_assert(s && sym); _sl = strlen(sym); @@ -2328,7 +2349,7 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { /* int_t */ tmp.integer = (int_t)strtol(sym, &conv_suc, 0); if(*conv_suc == '\0') { - *value = tmp.any; + memcpy(*value, tmp.any, sizeof(_raw_t)); result = _DT_INT; @@ -2337,7 +2358,7 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { /* real_t */ tmp.float_point = (real_t)strtod(sym, &conv_suc); if(*conv_suc == '\0') { - *value = tmp.any; + memcpy(*value, tmp.any, sizeof(_raw_t)); result = _DT_REAL; @@ -2353,7 +2374,7 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { glbsyminscope = _ht_find((_ht_node_t*)s->global_var_dict, sym); if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_ARRAY) { tmp.obj = (_object_t*)(glbsyminscope->data); - *value = (void*)(intptr_t)(tmp.obj->data.array->type); + memcpy(*value, &(tmp.obj->data.array->type), sizeof(tmp.obj->data.array->type)); result = _DT_ARRAY; @@ -2361,7 +2382,8 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { } if(context->last_symbol && context->last_symbol->type == _DT_FUNC) { if(strcmp("DIM", context->last_symbol->data.func->name) == 0) { - *value = (void*)(intptr_t)(sym[_sl - 1] == '$' ? _DT_STRING : _DT_REAL); + en = (sym[_sl - 1] == '$' ? _DT_STRING : _DT_REAL); + memcpy(*value, &en, sizeof(en)); result = _DT_ARRAY; @@ -2369,10 +2391,12 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { } } /* _func_t */ - if(context->last_symbol && ((context->last_symbol->type == _DT_FUNC && context->last_symbol->data.func->pointer != _core_close_bracket) || - context->last_symbol->type == _DT_SEP)) { + if(!context->last_symbol || + (context->last_symbol && ((context->last_symbol->type == _DT_FUNC && context->last_symbol->data.func->pointer != _core_close_bracket) || + context->last_symbol->type == _DT_SEP))) { if(strcmp("-", sym) == 0) { - *value = (void*)(intptr_t)(_core_neg); + ptr = (intptr_t)_core_neg; + memcpy(*value, &ptr, sizeof(intptr_t)); result = _DT_FUNC; @@ -2382,7 +2406,8 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { lclsyminscope = _ht_find((_ht_node_t*)s->local_func_dict, sym); glbsyminscope = _ht_find((_ht_node_t*)s->global_func_dict, sym); if(lclsyminscope || glbsyminscope) { - *value = lclsyminscope ? lclsyminscope->data : glbsyminscope->data; + ptr = lclsyminscope ? (intptr_t)lclsyminscope->data : (intptr_t)glbsyminscope->data; + memcpy(*value, &ptr, sizeof(intptr_t)); result = _DT_FUNC; @@ -2404,7 +2429,7 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { glbsyminscope = _ht_find((_ht_node_t*)s->global_var_dict, sym); if(glbsyminscope) { if(((_object_t*)glbsyminscope->data)->type != _DT_LABEL) { - *value = glbsyminscope->data; + memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); result = _DT_VAR; @@ -2416,7 +2441,7 @@ _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, void** value) { if(!context->last_symbol || context->last_symbol->type == _DT_EOS) { glbsyminscope = _ht_find((_ht_node_t*)s->global_var_dict, sym); if(glbsyminscope) { - *value = glbsyminscope->data; + memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); } result = _DT_LABEL; @@ -2442,11 +2467,13 @@ int _parse_char(mb_interpreter_t* s, char c, int pos, unsigned short row, unsign /* Parse a char */ int result = MB_FUNC_OK; _parsing_context_t* context = 0; + char last_char = '\0'; mb_assert(s && s->parsing_context); context = (_parsing_context_t*)(s->parsing_context); + last_char = context->current_char; context->current_char = c; if(context->parsing_state == _PS_NORMAL) { @@ -2478,9 +2505,13 @@ int _parse_char(mb_interpreter_t* s, char c, int pos, unsigned short row, unsign if(_is_identifier_char(c)) { result += _append_char_to_symbol(s, c); } else if(_is_operator_char(c)) { - context->symbol_state = _SS_OPERATOR; - result += _cut_symbol(s, pos, row, col); - result += _append_char_to_symbol(s, c); + if((last_char == 'e' || last_char == 'E') && c == '-') { + result += _append_char_to_symbol(s, c); + } else { + context->symbol_state = _SS_OPERATOR; + result += _cut_symbol(s, pos, row, col); + result += _append_char_to_symbol(s, c); + } } else { _handle_error(s, SE_PS_INVALID_CHAR, pos, row, col, MB_FUNC_ERR, _exit, result); } @@ -4217,9 +4248,13 @@ int _core_neg(mb_interpreter_t* s, void** l) { running = (_running_context_t*)(s->running_context); - inep = (int*)_ls_back(running->in_neg_expr)->data; + if(!_ls_empty(running->in_neg_expr)) { + inep = (int*)_ls_back(running->in_neg_expr)->data; + } - (*inep)++; + if(inep) { + (*inep)++; + } mb_check(mb_attempt_func_begin(s, l)); @@ -4227,7 +4262,9 @@ int _core_neg(mb_interpreter_t* s, void** l) { mb_check(mb_attempt_func_end(s, l)); - (*inep)--; + if(inep) { + (*inep)--; + } switch(arg.type) { case MB_DT_INT: diff --git a/core/my_basic.h b/core/my_basic.h index a0174c5..f5582af 100755 --- a/core/my_basic.h +++ b/core/my_basic.h @@ -1,265 +1,267 @@ -/* -** This source file is part of MY-BASIC -** -** For the latest info, see https://github.com/paladin-t/my_basic/ -** -** Copyright (C) 2011 - 2015 W. Renxin -** -** Permission is hereby granted, free of charge, to any person obtaining a copy of -** this software and associated documentation files (the "Software"), to deal in -** the Software without restriction, including without limitation the rights to -** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -** the Software, and to permit persons to whom the Software is furnished to do so, -** subject to the following conditions: -** -** The above copyright notice and this permission notice shall be included in all -** copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef __MY_BASIC_H__ -#define __MY_BASIC_H__ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef MBAPI -# define MBAPI -#endif /* MBAPI */ - -#ifndef MB_COMPACT_MODE -# define MB_COMPACT_MODE -#endif /* MB_COMPACT_MODE */ - -#ifdef MB_COMPACT_MODE -# pragma pack(1) -#endif /* MB_COMPACT_MODE */ - -#ifndef true -# define true (!0) -#endif -#ifndef false -# define false (0) -#endif - -#ifndef bool_t -# define bool_t int -#endif -#ifndef byte_t -# define byte_t unsigned char -#endif -#ifndef int_t -# define int_t int -#endif -#ifndef real_t -# define real_t float -#endif - -#ifndef _MSC_VER -# ifndef _strcmpi -# ifdef __BORLANDC__ -# define _strcmpi stricmp -# else /* __BORLANDC__*/ -# define _strcmpi strcasecmp -# endif /* __BORLANDC__ */ -# endif /* _strcmpi */ -#endif /* _MSC_VER */ - -#ifndef mb_assert -# define mb_assert(__a) do { ((void)(__a)); assert(__a); } while(0) -#endif /* mb_assert */ - -#ifndef mb_static_assert -# define _static_assert_impl(cond, msg) typedef char static_assertion_##msg[(!!(cond)) * 2 - 1] -# define _compile_time_assert3(x, l) _static_assert_impl(x, static_assertion_at_line_##l) -# define _compile_time_assert2(x, l) _compile_time_assert3(x, l) -# define mb_static_assert(x) _compile_time_assert2(x, __LINE__) -#endif /* mb_static_assert */ - -#ifndef mb_unrefvar -# define mb_unrefvar(__v) ((void)(__v)) -#endif /* mb_unrefvar */ - -#ifndef MB_CODES -# define MB_CODES -# define MB_FUNC_OK 0 -# define MB_FUNC_BYE 1001 -# define MB_FUNC_WARNING 1002 -# define MB_FUNC_ERR 1003 -# define MB_FUNC_END 1004 -# define MB_FUNC_SUSPEND 1005 -# define MB_PARSING_ERR 3001 -# define MB_LOOP_BREAK 5001 -# define MB_LOOP_CONTINUE 5002 -# define MB_SUB_RETURN 5101 -# define MB_EXTENDED_ABORT 9001 -#endif /* MB_CODES */ - -#ifndef mb_check -# define mb_check(__r) { int __hr = __r; if(__hr != MB_FUNC_OK) { return __hr; } } -#endif /* mb_check */ - -#ifndef mb_reg_fun -# define mb_reg_fun(__s, __f) mb_register_func(__s, #__f, __f) -#endif /* mb_reg_fun */ -#ifndef mb_rem_fun -# define mb_rem_fun(__s, __f) mb_remove_func(__s, #__f) -#endif /* mb_rem_fun */ -#ifndef mb_rem_res_fun -# define mb_rem_res_fun(__s, __f) mb_remove_reserved_func(__s, #__f) -#endif /* mb_rem_res_fun */ - -struct mb_interpreter_t; - -typedef enum mb_error_e { - SE_NO_ERR = 0, - /** Common */ - SE_CM_MB_OPEN_FAILED, - SE_CM_FUNC_EXISTS, - SE_CM_FUNC_NOT_EXISTS, - /** Parsing */ - SE_PS_FILE_OPEN_FAILED, - SE_PS_SYMBOL_TOO_LONG, - SE_PS_INVALID_CHAR, - /** Running */ - SE_RN_NOT_SUPPORTED, - SE_RN_EMPTY_PROGRAM, - SE_RN_SYNTAX, - SE_RN_INVALID_DATA_TYPE, - SE_RN_TYPE_NOT_MATCH, - SE_RN_ILLEGAL_BOUND, - SE_RN_DIMENSION_TOO_MUCH, - SE_RN_OPERATION_FAILED, - SE_RN_DIMENSION_OUT_OF_BOUND, - SE_RN_ARRAY_OUT_OF_BOUND, - SE_RN_LABEL_NOT_EXISTS, - SE_RN_NO_RETURN_POINT, - SE_RN_COLON_EXPECTED, - SE_RN_COMMA_OR_SEMICOLON_EXPECTED, - SE_RN_ARRAY_IDENTIFIER_EXPECTED, - SE_RN_OPEN_BRACKET_EXPECTED, - SE_RN_CLOSE_BRACKET_EXPECTED, - SE_RN_ARRAY_SUBSCRIPT_EXPECTED, - SE_RN_STRUCTURE_NOT_COMPLETED, - SE_RN_FUNCTION_EXPECTED, - SE_RN_STRING_EXPECTED, - SE_RN_VAR_OR_ARRAY_EXPECTED, - SE_RN_ASSIGN_OPERATOR_EXPECTED, - SE_RN_INTEGER_EXPECTED, - SE_RN_ELSE_EXPECTED, - SE_RN_TO_EXPECTED, - SE_RN_NEXT_EXPECTED, - SE_RN_UNTIL_EXPECTED, - SE_RN_LOOP_VAR_EXPECTED, - SE_RN_JUMP_LABEL_EXPECTED, - SE_RN_VARIABLE_EXPECTED, - SE_RN_INVALID_ID_USAGE, - SE_RN_OPERATOR_EXPECTED, - SE_RN_CALCULATION_ERROR, - SE_RN_DIVIDE_BY_ZERO, - SE_RN_MOD_BY_ZERO, - SE_RN_INVALID_EXPRESSION, - SE_RN_OUT_OF_MEMORY, - /** Extended abort */ - SE_EA_EXTENDED_ABORT, - /** Extra */ - SE_COUNT -} mb_error_e; - -typedef enum mb_data_e { - MB_DT_NIL = -1, - MB_DT_INT = 0, - MB_DT_REAL, - MB_DT_STRING -} mb_data_e; - -typedef union mb_value_u { - int_t integer; - real_t float_point; - char* string; -} mb_value_u; - -typedef struct mb_value_t { - mb_data_e type; - mb_value_u value; -} mb_value_t; - -typedef void (* mb_error_handler_t)(struct mb_interpreter_t*, enum mb_error_e, char*, int, unsigned short, unsigned short, int); -typedef int (* mb_func_t)(struct mb_interpreter_t*, void**); -typedef int (* mb_print_func_t)(const char*, ...); -typedef int (* mb_input_func_t)(char*, int); - -typedef struct mb_interpreter_t { - void* local_func_dict; - void* global_func_dict; - void* global_var_dict; - void* ast; - void* parsing_context; - void* running_context; - mb_error_e last_error; - int last_error_pos; - unsigned short last_error_row; - unsigned short last_error_col; - mb_error_handler_t error_handler; - mb_print_func_t printer; - mb_input_func_t inputer; - void* userdata; -} mb_interpreter_t; - -MBAPI unsigned int mb_ver(void); -MBAPI const char* mb_ver_string(void); - -MBAPI int mb_init(void); -MBAPI int mb_dispose(void); -MBAPI int mb_open(mb_interpreter_t** s); -MBAPI int mb_close(mb_interpreter_t** s); -MBAPI int mb_reset(mb_interpreter_t** s, bool_t clrf); - -MBAPI int mb_register_func(mb_interpreter_t* s, const char* n, mb_func_t f); -MBAPI int mb_remove_func(mb_interpreter_t* s, const char* n); -MBAPI int mb_remove_reserved_func(mb_interpreter_t* s, const char* n); - -MBAPI int mb_attempt_func_begin(mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_func_end(mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_open_bracket(mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_close_bracket(mb_interpreter_t* s, void** l); -MBAPI int mb_pop_int(mb_interpreter_t* s, void** l, int_t* val); -MBAPI int mb_pop_real(mb_interpreter_t* s, void** l, real_t* val); -MBAPI int mb_pop_string(mb_interpreter_t* s, void** l, char** val); -MBAPI int mb_pop_value(mb_interpreter_t* s, void** l, mb_value_t* val); -MBAPI int mb_push_int(mb_interpreter_t* s, void** l, int_t val); -MBAPI int mb_push_real(mb_interpreter_t* s, void** l, real_t val); -MBAPI int mb_push_string(mb_interpreter_t* s, void** l, char* val); -MBAPI int mb_push_value(mb_interpreter_t* s, void** l, mb_value_t val); - -MBAPI int mb_load_string(mb_interpreter_t* s, const char* l); -MBAPI int mb_load_file(mb_interpreter_t* s, const char* f); -MBAPI int mb_run(mb_interpreter_t* s); -MBAPI int mb_suspend(mb_interpreter_t* s, void** l); - -MBAPI mb_error_e mb_get_last_error(mb_interpreter_t* s); -MBAPI const char* mb_get_error_desc(mb_error_e err); -MBAPI int mb_set_error_handler(mb_interpreter_t* s, mb_error_handler_t h); -MBAPI int mb_set_printer(mb_interpreter_t* s, mb_print_func_t p); -MBAPI int mb_set_inputer(mb_interpreter_t* s, mb_input_func_t p); - -MBAPI int mb_gets(char* buf, int s); - -MBAPI char* mb_strdup(char* val, unsigned size); - -#ifdef MB_COMPACT_MODE -# pragma pack() -#endif /* MB_COMPACT_MODE */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __MY_BASIC_H__ */ +/* +** This source file is part of MY-BASIC +** +** For the latest info, see https://github.com/paladin-t/my_basic/ +** +** Copyright (C) 2011 - 2015 W. Renxin +** +** Permission is hereby granted, free of charge, to any person obtaining a copy of +** this software and associated documentation files (the "Software"), to deal in +** the Software without restriction, including without limitation the rights to +** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +** the Software, and to permit persons to whom the Software is furnished to do so, +** subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MY_BASIC_H__ +#define __MY_BASIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MBAPI +# define MBAPI +#endif /* MBAPI */ + +#ifndef MB_COMPACT_MODE +# define MB_COMPACT_MODE +#endif /* MB_COMPACT_MODE */ + +#ifdef MB_COMPACT_MODE +# pragma pack(1) +#endif /* MB_COMPACT_MODE */ + +#ifndef true +# define true (!0) +#endif +#ifndef false +# define false (0) +#endif + +#ifndef bool_t +# define bool_t int +#endif +#ifndef byte_t +# define byte_t unsigned char +#endif +#ifndef int_t +# define int_t int +#endif +#ifndef real_t +# define real_t float +#endif + +#ifndef _MSC_VER +# ifndef _strcmpi +# ifdef __BORLANDC__ +# define _strcmpi stricmp +# elif defined __POCC__ +# define _strcmpi _stricmp +# else /* __BORLANDC__*/ +# define _strcmpi strcasecmp +# endif /* __BORLANDC__ */ +# endif /* _strcmpi */ +#endif /* _MSC_VER */ + +#ifndef mb_assert +# define mb_assert(__a) do { ((void)(__a)); assert(__a); } while(0) +#endif /* mb_assert */ + +#ifndef mb_static_assert +# define _static_assert_impl(cond, msg) typedef char static_assertion_##msg[(!!(cond)) * 2 - 1] +# define _compile_time_assert3(x, l) _static_assert_impl(x, static_assertion_at_line_##l) +# define _compile_time_assert2(x, l) _compile_time_assert3(x, l) +# define mb_static_assert(x) _compile_time_assert2(x, __LINE__) +#endif /* mb_static_assert */ + +#ifndef mb_unrefvar +# define mb_unrefvar(__v) ((void)(__v)) +#endif /* mb_unrefvar */ + +#ifndef MB_CODES +# define MB_CODES +# define MB_FUNC_OK 0 +# define MB_FUNC_BYE 1001 +# define MB_FUNC_WARNING 1002 +# define MB_FUNC_ERR 1003 +# define MB_FUNC_END 1004 +# define MB_FUNC_SUSPEND 1005 +# define MB_PARSING_ERR 3001 +# define MB_LOOP_BREAK 5001 +# define MB_LOOP_CONTINUE 5002 +# define MB_SUB_RETURN 5101 +# define MB_EXTENDED_ABORT 9001 +#endif /* MB_CODES */ + +#ifndef mb_check +# define mb_check(__r) { int __hr = __r; if(__hr != MB_FUNC_OK) { return __hr; } } +#endif /* mb_check */ + +#ifndef mb_reg_fun +# define mb_reg_fun(__s, __f) mb_register_func(__s, #__f, __f) +#endif /* mb_reg_fun */ +#ifndef mb_rem_fun +# define mb_rem_fun(__s, __f) mb_remove_func(__s, #__f) +#endif /* mb_rem_fun */ +#ifndef mb_rem_res_fun +# define mb_rem_res_fun(__s, __f) mb_remove_reserved_func(__s, #__f) +#endif /* mb_rem_res_fun */ + +struct mb_interpreter_t; + +typedef enum mb_error_e { + SE_NO_ERR = 0, + /** Common */ + SE_CM_MB_OPEN_FAILED, + SE_CM_FUNC_EXISTS, + SE_CM_FUNC_NOT_EXISTS, + /** Parsing */ + SE_PS_FILE_OPEN_FAILED, + SE_PS_SYMBOL_TOO_LONG, + SE_PS_INVALID_CHAR, + /** Running */ + SE_RN_NOT_SUPPORTED, + SE_RN_EMPTY_PROGRAM, + SE_RN_SYNTAX, + SE_RN_INVALID_DATA_TYPE, + SE_RN_TYPE_NOT_MATCH, + SE_RN_ILLEGAL_BOUND, + SE_RN_DIMENSION_TOO_MUCH, + SE_RN_OPERATION_FAILED, + SE_RN_DIMENSION_OUT_OF_BOUND, + SE_RN_ARRAY_OUT_OF_BOUND, + SE_RN_LABEL_NOT_EXISTS, + SE_RN_NO_RETURN_POINT, + SE_RN_COLON_EXPECTED, + SE_RN_COMMA_OR_SEMICOLON_EXPECTED, + SE_RN_ARRAY_IDENTIFIER_EXPECTED, + SE_RN_OPEN_BRACKET_EXPECTED, + SE_RN_CLOSE_BRACKET_EXPECTED, + SE_RN_ARRAY_SUBSCRIPT_EXPECTED, + SE_RN_STRUCTURE_NOT_COMPLETED, + SE_RN_FUNCTION_EXPECTED, + SE_RN_STRING_EXPECTED, + SE_RN_VAR_OR_ARRAY_EXPECTED, + SE_RN_ASSIGN_OPERATOR_EXPECTED, + SE_RN_INTEGER_EXPECTED, + SE_RN_ELSE_EXPECTED, + SE_RN_TO_EXPECTED, + SE_RN_NEXT_EXPECTED, + SE_RN_UNTIL_EXPECTED, + SE_RN_LOOP_VAR_EXPECTED, + SE_RN_JUMP_LABEL_EXPECTED, + SE_RN_VARIABLE_EXPECTED, + SE_RN_INVALID_ID_USAGE, + SE_RN_OPERATOR_EXPECTED, + SE_RN_CALCULATION_ERROR, + SE_RN_DIVIDE_BY_ZERO, + SE_RN_MOD_BY_ZERO, + SE_RN_INVALID_EXPRESSION, + SE_RN_OUT_OF_MEMORY, + /** Extended abort */ + SE_EA_EXTENDED_ABORT, + /** Extra */ + SE_COUNT +} mb_error_e; + +typedef enum mb_data_e { + MB_DT_NIL = -1, + MB_DT_INT = 0, + MB_DT_REAL, + MB_DT_STRING +} mb_data_e; + +typedef union mb_value_u { + int_t integer; + real_t float_point; + char* string; +} mb_value_u; + +typedef struct mb_value_t { + mb_data_e type; + mb_value_u value; +} mb_value_t; + +typedef void (* mb_error_handler_t)(struct mb_interpreter_t*, enum mb_error_e, char*, int, unsigned short, unsigned short, int); +typedef int (* mb_func_t)(struct mb_interpreter_t*, void**); +typedef int (* mb_print_func_t)(const char*, ...); +typedef int (* mb_input_func_t)(char*, int); + +typedef struct mb_interpreter_t { + void* local_func_dict; + void* global_func_dict; + void* global_var_dict; + void* ast; + void* parsing_context; + void* running_context; + mb_error_e last_error; + int last_error_pos; + unsigned short last_error_row; + unsigned short last_error_col; + mb_error_handler_t error_handler; + mb_print_func_t printer; + mb_input_func_t inputer; + void* userdata; +} mb_interpreter_t; + +MBAPI unsigned int mb_ver(void); +MBAPI const char* mb_ver_string(void); + +MBAPI int mb_init(void); +MBAPI int mb_dispose(void); +MBAPI int mb_open(mb_interpreter_t** s); +MBAPI int mb_close(mb_interpreter_t** s); +MBAPI int mb_reset(mb_interpreter_t** s, bool_t clrf); + +MBAPI int mb_register_func(mb_interpreter_t* s, const char* n, mb_func_t f); +MBAPI int mb_remove_func(mb_interpreter_t* s, const char* n); +MBAPI int mb_remove_reserved_func(mb_interpreter_t* s, const char* n); + +MBAPI int mb_attempt_func_begin(mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_func_end(mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_open_bracket(mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_close_bracket(mb_interpreter_t* s, void** l); +MBAPI int mb_pop_int(mb_interpreter_t* s, void** l, int_t* val); +MBAPI int mb_pop_real(mb_interpreter_t* s, void** l, real_t* val); +MBAPI int mb_pop_string(mb_interpreter_t* s, void** l, char** val); +MBAPI int mb_pop_value(mb_interpreter_t* s, void** l, mb_value_t* val); +MBAPI int mb_push_int(mb_interpreter_t* s, void** l, int_t val); +MBAPI int mb_push_real(mb_interpreter_t* s, void** l, real_t val); +MBAPI int mb_push_string(mb_interpreter_t* s, void** l, char* val); +MBAPI int mb_push_value(mb_interpreter_t* s, void** l, mb_value_t val); + +MBAPI int mb_load_string(mb_interpreter_t* s, const char* l); +MBAPI int mb_load_file(mb_interpreter_t* s, const char* f); +MBAPI int mb_run(mb_interpreter_t* s); +MBAPI int mb_suspend(mb_interpreter_t* s, void** l); + +MBAPI mb_error_e mb_get_last_error(mb_interpreter_t* s); +MBAPI const char* mb_get_error_desc(mb_error_e err); +MBAPI int mb_set_error_handler(mb_interpreter_t* s, mb_error_handler_t h); +MBAPI int mb_set_printer(mb_interpreter_t* s, mb_print_func_t p); +MBAPI int mb_set_inputer(mb_interpreter_t* s, mb_input_func_t p); + +MBAPI int mb_gets(char* buf, int s); + +MBAPI char* mb_strdup(char* val, unsigned size); + +#ifdef MB_COMPACT_MODE +# pragma pack() +#endif /* MB_COMPACT_MODE */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MY_BASIC_H__ */ diff --git a/output/my_basic.exe b/output/my_basic.exe index 740a3fc..b53ac57 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 c09e1ff..66e413a 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 1de3c05..c4883db 100755 --- a/resource/my_basic.rc +++ b/resource/my_basic.rc @@ -81,13 +81,13 @@ BEGIN VALUE "Comments", "MY-BASIC" VALUE "CompanyName", "W. Renxin" VALUE "FileDescription", "MY-BASIC interpreter" - VALUE "FileVersion", "1, 0, 0, 46" + VALUE "FileVersion", "1, 0, 0, 47" VALUE "InternalName", "my_basic" VALUE "LegalCopyright", "Copyright (C) 2011 - 2015 W. Renxin" VALUE "LegalTrademarks", "MY-BASIC" VALUE "OriginalFilename", "my_basic.exe" VALUE "ProductName", "MY-BASIC" - VALUE "ProductVersion", "1, 0, 0, 46" + VALUE "ProductVersion", "1, 0, 0, 47" END END BLOCK "VarFileInfo" diff --git a/shell/main.c b/shell/main.c index 15fdc5f..322e422 100755 --- a/shell/main.c +++ b/shell/main.c @@ -1,439 +1,444 @@ -/* -** This source file is part of MY-BASIC -** -** For the latest info, see https://github.com/paladin-t/my_basic/ -** -** Copyright (C) 2011 - 2015 W. Renxin -** -** Permission is hereby granted, free of charge, to any person obtaining a copy of -** this software and associated documentation files (the "Software"), to deal in -** the Software without restriction, including without limitation the rights to -** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -** the Software, and to permit persons to whom the Software is furnished to do so, -** subject to the following conditions: -** -** The above copyright notice and this permission notice shall be included in all -** copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifdef _MSC_VER -# ifndef _CRT_SECURE_NO_WARNINGS -# define _CRT_SECURE_NO_WARNINGS -# endif /* _CRT_SECURE_NO_WARNINGS */ -#endif /* _MSC_VER */ - -#include "../core/my_basic.h" -#ifdef _MSC_VER -# include -# include -#elif !defined __BORLANDC__ -# include -#endif /* _MSC_VER */ -#include -#include -#include -#include - -#ifdef _MSC_VER -# pragma warning(disable : 4127) -# pragma warning(disable : 4996) -#endif /* _MSC_VER */ - -#ifdef __BORLANDC__ -# pragma warn -8004 -# pragma warn -8008 -# pragma warn -8066 -#endif /* __BORLANDC__ */ - -#define _MAX_LINE_LENGTH 256 -#define _str_eq(__str1, __str2) (_strcmpi(__str1, __str2) == 0) - -#define _LINE_INC_STEP 16 - -#define _NO_END(s) (MB_FUNC_OK == s || MB_FUNC_SUSPEND == s || MB_FUNC_WARNING == s || MB_FUNC_ERR == s || MB_FUNC_END == s) - -typedef struct _code_line_t { - char** lines; - int count; - int size; -} _code_line_t; - -static mb_interpreter_t* bas = 0; - -static _code_line_t* c = 0; - -static _code_line_t* _create_code(void) { - _code_line_t* result = (_code_line_t*)malloc(sizeof(_code_line_t)); - result->count = 0; - result->size = _LINE_INC_STEP; - result->lines = (char**)malloc(sizeof(char*) * result->size); - - return result; -} - -static void _destroy_code(_code_line_t* code) { - int i = 0; - mb_assert(code); - for(i = 0; i < code->count; ++i) { - free(code->lines[i]); - } - free(code->lines); - free(code); -} - -static void _clear_code(_code_line_t* code) { - int i = 0; - mb_assert(code); - for(i = 0; i < code->count; ++i) { - free(code->lines[i]); - } - code->count = 0; -} - -static void _append_line(_code_line_t* code, char* txt) { - mb_assert(code && txt); - if(code->count + 1 == code->size) { - code->size += _LINE_INC_STEP; - code->lines = (char**)realloc(code->lines, sizeof(char*) * code->size); - } - code->lines[code->count++] = strdup(txt); -} - -static char* _get_code(_code_line_t* code) { - char* result = 0; - int i = 0; - mb_assert(code); - result = (char*)malloc((_MAX_LINE_LENGTH + 2) * code->count + 1); - result[0] = '\0'; - for(i = 0; i < code->count; ++i) { - result = strcat(result, code->lines[i]); - if(i != code->count - 1) { - result = strcat(result, "\r\n"); - } - } - - return result; -} - -static void _set_code(_code_line_t* code, char* txt) { - char* cursor = 0; - char _c = '\0'; - mb_assert(code); - if(!txt) { - return; - } - _clear_code(code); - cursor = txt; - do { - _c = *cursor; - if(_c == '\r' || _c == '\n' || _c == '\0') { - cursor[0] = '\0'; - if(_c == '\r' && *(cursor + 1) == '\n') { - ++cursor; - } - _append_line(code, txt); - txt = cursor + 1; - } - ++cursor; - } while(_c); -} - -static char* _load_file(const char* path) { - FILE* fp = 0; - char* result = 0; - long curpos = 0; - long l = 0; - mb_assert(path); - fp = fopen(path, "rb"); - if(fp) { - curpos = ftell(fp); - fseek(fp, 0L, SEEK_END); - l = ftell(fp); - fseek(fp, curpos, SEEK_SET); - result = (char*)malloc((size_t)(l + 1)); - mb_assert(result); - fread(result, 1, l, fp); - fclose(fp); - result[l] = '\0'; - } - - return result; -} - -static int _save_file(const char* path, const char* txt) { - FILE* fp = 0; - mb_assert(path && txt); - fp = fopen(path, "wb"); - if(fp) { - fwrite(txt, sizeof(char), strlen(txt), fp); - fclose(fp); - - return 1; - } - - return 0; -} - -static int beep(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - mb_check(mb_attempt_func_begin(s, l)); - mb_check(mb_attempt_func_end(s, l)); - - putchar('\a'); - - return result; -} - -static void _on_error(mb_interpreter_t* s, mb_error_e e, char* m, int p, unsigned short row, unsigned short col, int abort_code) { - mb_unrefvar(s); - if(SE_NO_ERR != e) { - printf("Error:\n [POS] %d, [ROW] %d, [COL] %d,\n [CODE] %d, [MESSAGE] %s, [ABORT CODE] %d\n", p, row, col, e, m, abort_code); - } -} - -static void _on_startup(void) { - c = _create_code(); - - mb_init(); - - mb_open(&bas); - mb_set_error_handler(bas, _on_error); - - mb_reg_fun(bas, beep); -} - -static void _on_exit(void) { - mb_close(&bas); - - mb_dispose(); - - _destroy_code(c); - c = 0; - -#if defined _MSC_VER && !defined _WIN64 - if(0 != _CrtDumpMemoryLeaks()) { _asm { int 3 } } -#endif /* _MSC_VER && !_WIN64 */ -} - -static void _clear_screen(void) { -#ifdef _MSC_VER - system("cls"); -#else /* _MSC_VER */ - system("clear"); -#endif /* _MSC_VER */ -} - -static int _new_program(void) { - _clear_code(c); - - return mb_reset(&bas, false); -} - -static void _list_program(const char* sn, const char* cn) { - long lsn = 0; - long lcn = 0; - mb_assert(sn && cn); - lsn = atoi(sn); - lcn = atoi(cn); - if(lsn == 0 && lcn == 0) { - char* txt = _get_code(c); - printf("%s\n", txt); - free(txt); - } else { - long i = 0; - long e = 0; - if(lsn < 1 || lsn > c->count) { - printf("Line number %ld out of bound.\n", lsn); - - return; - } - if(lcn < 0) { - printf("Invalid line count %ld.\n", lcn); - - return; - } - --lsn; - e = lcn ? lsn + lcn : c->count; - for(i = lsn; i < e; ++i) { - if(i >= c->count) { - break; - } - printf("%s\n", c->lines[i]); - } - } -} - -static void _edit_program(const char* no) { - char line[_MAX_LINE_LENGTH]; - long lno = 0; - mb_assert(no); - lno = atoi(no); - if(lno < 1 || lno > c->count) { - printf("Line number %ld out of bound.\n", lno); - - return; - } - --lno; - memset(line, 0, _MAX_LINE_LENGTH); - printf("%ld]", lno + 1); - mb_gets(line, _MAX_LINE_LENGTH); - c->lines[lno] = (char*)realloc(c->lines[lno], strlen(line) + 1); - strcpy(c->lines[lno], line); -} - -static void _load_program(const char* path) { - char* txt = _load_file(path); - if(txt) { - _new_program(); - _set_code(c, txt); - free(txt); - if(c->count == 1) { - printf("Load done. %d line loaded.\n", c->count); - } else { - printf("Load done. %d lines loaded.\n", c->count); - } - } else { - printf("Cannot load file \"%s\"\n", path); - } -} - -static void _save_program(const char* path) { - char* txt = _get_code(c); - if(!_save_file(path, txt)) { - printf("Cannot save file \"%s\"\n", path); - } else { - if(c->count == 1) { - printf("Save done. %d line saved.\n", c->count); - } else { - printf("Save done. %d lines saved.\n", c->count); - } - } - free(txt); -} - -static void _kill_program(const char* path) { - if(!unlink(path)) { - printf("Delete file \"%s\" successfully.\n", path); - } else { - printf("Delete file \"%s\" failed.\n", path); - } -} - -static void _show_tip(void) { - printf("MY-BASIC Interpreter Shell - %s.\n", mb_ver_string()); - printf("Copyright (C) 2011 - 2015 W. Renxin. All Rights Reserved.\n"); - printf("For more information, see https://github.com/paladin-t/my_basic/.\n"); - printf("Input HELP and hint enter to view help information.\n"); -} - -static void _show_help(void) { - printf("Commands:\n"); - printf(" CLS - Clear screen\n"); - printf(" NEW - Clear current program\n"); - printf(" RUN - Run current program\n"); - printf(" BYE - Quit interpreter\n"); - printf(" LIST - List current program\n"); - printf(" Usage: LIST [l [n]], l is start line number, n is line count\n"); - printf(" EDIT - Edit a line in current program\n"); - printf(" Usage: EDIT n, n is line number\n"); - printf(" LOAD - Load a file as current program\n"); - printf(" Usage: LOAD *.*\n"); - printf(" SAVE - Save current program to a file\n"); - printf(" Usage: SAVE *.*\n"); - printf(" KILL - Delete a file\n"); - printf(" Usage: KILL *.*\n"); -} - -static int _do_line(void) { - int result = MB_FUNC_OK; - char line[_MAX_LINE_LENGTH]; - char dup[_MAX_LINE_LENGTH]; - - mb_assert(bas); - - memset(line, 0, _MAX_LINE_LENGTH); - printf("]"); - mb_gets(line, _MAX_LINE_LENGTH); - - memcpy(dup, line, _MAX_LINE_LENGTH); - strtok(line, " "); - - if(_str_eq(line, "")) { - /* Do nothing */ - } else if(_str_eq(line, "HELP")) { - _show_help(); - } else if(_str_eq(line, "CLS")) { - _clear_screen(); - } else if(_str_eq(line, "NEW")) { - result = _new_program(); - } else if(_str_eq(line, "RUN")) { - char* txt = _get_code(c); - result = mb_reset(&bas, false); - result = mb_load_string(bas, txt); - free(txt); - result = mb_run(bas); - printf("\n"); - } else if(_str_eq(line, "BYE")) { - result = MB_FUNC_BYE; - } else if(_str_eq(line, "LIST")) { - char* sn = line + strlen(line) + 1; - char* cn = 0; - strtok(sn, " "); - cn = sn + strlen(sn) + 1; - _list_program(sn, cn); - } else if(_str_eq(line, "EDIT")) { - char* no = line + strlen(line) + 1; - _edit_program(no); - } else if(_str_eq(line, "LOAD")) { - char* path = line + strlen(line) + 1; - _load_program(path); - } else if(_str_eq(line, "SAVE")) { - char* path = line + strlen(line) + 1; - _save_program(path); - } else if(_str_eq(line, "KILL")) { - char* path = line + strlen(line) + 1; - _kill_program(path); - } else { - _append_line(c, dup); - } - - return result; -} - -int main(int argc, char* argv[]) { - int status = 0; - -#if defined _MSC_VER && !defined _WIN64 - _CrtSetBreakAlloc(0); -#endif /* _MSC_VER && !_WIN64 */ - - atexit(_on_exit); - - _on_startup(); - - if(argc == 1) { - _show_tip(); - do { - status = _do_line(); - } while(_NO_END(status)); - } else if(argc == 2) { - if(mb_load_file(bas, argv[1]) == MB_FUNC_OK) { - mb_run(bas); - } - } else { - printf("Unknown arguments\n"); - _show_tip(); - } - - return 0; -} +/* +** This source file is part of MY-BASIC +** +** For the latest info, see https://github.com/paladin-t/my_basic/ +** +** Copyright (C) 2011 - 2015 W. Renxin +** +** Permission is hereby granted, free of charge, to any person obtaining a copy of +** this software and associated documentation files (the "Software"), to deal in +** the Software without restriction, including without limitation the rights to +** use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +** the Software, and to permit persons to whom the Software is furnished to do so, +** subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +** FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifdef _MSC_VER +# ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +# endif /* _CRT_SECURE_NO_WARNINGS */ +#endif /* _MSC_VER */ + +#include "../core/my_basic.h" +#ifdef _MSC_VER +# include +# include +#elif !defined __BORLANDC__ +# include +#endif /* _MSC_VER */ +#include +#include +#include +#include + +#ifdef _MSC_VER +# pragma warning(disable : 4127) +# pragma warning(disable : 4996) +#endif /* _MSC_VER */ + +#ifdef __BORLANDC__ +# pragma warn -8004 +# pragma warn -8008 +# pragma warn -8066 +#endif /* __BORLANDC__ */ + +#ifdef __POCC__ +# define unlink _unlink +# define strdup _strdup +#endif /* __POCC__ */ + +#define _MAX_LINE_LENGTH 256 +#define _str_eq(__str1, __str2) (_strcmpi(__str1, __str2) == 0) + +#define _LINE_INC_STEP 16 + +#define _NO_END(s) (MB_FUNC_OK == s || MB_FUNC_SUSPEND == s || MB_FUNC_WARNING == s || MB_FUNC_ERR == s || MB_FUNC_END == s) + +typedef struct _code_line_t { + char** lines; + int count; + int size; +} _code_line_t; + +static mb_interpreter_t* bas = 0; + +static _code_line_t* c = 0; + +static _code_line_t* _create_code(void) { + _code_line_t* result = (_code_line_t*)malloc(sizeof(_code_line_t)); + result->count = 0; + result->size = _LINE_INC_STEP; + result->lines = (char**)malloc(sizeof(char*) * result->size); + + return result; +} + +static void _destroy_code(_code_line_t* code) { + int i = 0; + mb_assert(code); + for(i = 0; i < code->count; ++i) { + free(code->lines[i]); + } + free(code->lines); + free(code); +} + +static void _clear_code(_code_line_t* code) { + int i = 0; + mb_assert(code); + for(i = 0; i < code->count; ++i) { + free(code->lines[i]); + } + code->count = 0; +} + +static void _append_line(_code_line_t* code, char* txt) { + mb_assert(code && txt); + if(code->count + 1 == code->size) { + code->size += _LINE_INC_STEP; + code->lines = (char**)realloc(code->lines, sizeof(char*) * code->size); + } + code->lines[code->count++] = strdup(txt); +} + +static char* _get_code(_code_line_t* code) { + char* result = 0; + int i = 0; + mb_assert(code); + result = (char*)malloc((_MAX_LINE_LENGTH + 2) * code->count + 1); + result[0] = '\0'; + for(i = 0; i < code->count; ++i) { + result = strcat(result, code->lines[i]); + if(i != code->count - 1) { + result = strcat(result, "\r\n"); + } + } + + return result; +} + +static void _set_code(_code_line_t* code, char* txt) { + char* cursor = 0; + char _c = '\0'; + mb_assert(code); + if(!txt) { + return; + } + _clear_code(code); + cursor = txt; + do { + _c = *cursor; + if(_c == '\r' || _c == '\n' || _c == '\0') { + cursor[0] = '\0'; + if(_c == '\r' && *(cursor + 1) == '\n') { + ++cursor; + } + _append_line(code, txt); + txt = cursor + 1; + } + ++cursor; + } while(_c); +} + +static char* _load_file(const char* path) { + FILE* fp = 0; + char* result = 0; + long curpos = 0; + long l = 0; + mb_assert(path); + fp = fopen(path, "rb"); + if(fp) { + curpos = ftell(fp); + fseek(fp, 0L, SEEK_END); + l = ftell(fp); + fseek(fp, curpos, SEEK_SET); + result = (char*)malloc((size_t)(l + 1)); + mb_assert(result); + fread(result, 1, l, fp); + fclose(fp); + result[l] = '\0'; + } + + return result; +} + +static int _save_file(const char* path, const char* txt) { + FILE* fp = 0; + mb_assert(path && txt); + fp = fopen(path, "wb"); + if(fp) { + fwrite(txt, sizeof(char), strlen(txt), fp); + fclose(fp); + + return 1; + } + + return 0; +} + +static int beep(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + mb_check(mb_attempt_func_begin(s, l)); + mb_check(mb_attempt_func_end(s, l)); + + putchar('\a'); + + return result; +} + +static void _on_error(mb_interpreter_t* s, mb_error_e e, char* m, int p, unsigned short row, unsigned short col, int abort_code) { + mb_unrefvar(s); + if(SE_NO_ERR != e) { + printf("Error:\n [POS] %d, [ROW] %d, [COL] %d,\n [CODE] %d, [MESSAGE] %s, [ABORT CODE] %d\n", p, row, col, e, m, abort_code); + } +} + +static void _on_startup(void) { + c = _create_code(); + + mb_init(); + + mb_open(&bas); + mb_set_error_handler(bas, _on_error); + + mb_reg_fun(bas, beep); +} + +static void _on_exit(void) { + mb_close(&bas); + + mb_dispose(); + + _destroy_code(c); + c = 0; + +#if defined _MSC_VER && !defined _WIN64 + if(0 != _CrtDumpMemoryLeaks()) { _asm { int 3 } } +#endif /* _MSC_VER && !_WIN64 */ +} + +static void _clear_screen(void) { +#ifdef _MSC_VER + system("cls"); +#else /* _MSC_VER */ + system("clear"); +#endif /* _MSC_VER */ +} + +static int _new_program(void) { + _clear_code(c); + + return mb_reset(&bas, false); +} + +static void _list_program(const char* sn, const char* cn) { + long lsn = 0; + long lcn = 0; + mb_assert(sn && cn); + lsn = atoi(sn); + lcn = atoi(cn); + if(lsn == 0 && lcn == 0) { + char* txt = _get_code(c); + printf("%s\n", txt); + free(txt); + } else { + long i = 0; + long e = 0; + if(lsn < 1 || lsn > c->count) { + printf("Line number %ld out of bound.\n", lsn); + + return; + } + if(lcn < 0) { + printf("Invalid line count %ld.\n", lcn); + + return; + } + --lsn; + e = lcn ? lsn + lcn : c->count; + for(i = lsn; i < e; ++i) { + if(i >= c->count) { + break; + } + printf("%s\n", c->lines[i]); + } + } +} + +static void _edit_program(const char* no) { + char line[_MAX_LINE_LENGTH]; + long lno = 0; + mb_assert(no); + lno = atoi(no); + if(lno < 1 || lno > c->count) { + printf("Line number %ld out of bound.\n", lno); + + return; + } + --lno; + memset(line, 0, _MAX_LINE_LENGTH); + printf("%ld]", lno + 1); + mb_gets(line, _MAX_LINE_LENGTH); + c->lines[lno] = (char*)realloc(c->lines[lno], strlen(line) + 1); + strcpy(c->lines[lno], line); +} + +static void _load_program(const char* path) { + char* txt = _load_file(path); + if(txt) { + _new_program(); + _set_code(c, txt); + free(txt); + if(c->count == 1) { + printf("Load done. %d line loaded.\n", c->count); + } else { + printf("Load done. %d lines loaded.\n", c->count); + } + } else { + printf("Cannot load file \"%s\"\n", path); + } +} + +static void _save_program(const char* path) { + char* txt = _get_code(c); + if(!_save_file(path, txt)) { + printf("Cannot save file \"%s\"\n", path); + } else { + if(c->count == 1) { + printf("Save done. %d line saved.\n", c->count); + } else { + printf("Save done. %d lines saved.\n", c->count); + } + } + free(txt); +} + +static void _kill_program(const char* path) { + if(!unlink(path)) { + printf("Delete file \"%s\" successfully.\n", path); + } else { + printf("Delete file \"%s\" failed.\n", path); + } +} + +static void _show_tip(void) { + printf("MY-BASIC Interpreter Shell - %s.\n", mb_ver_string()); + printf("Copyright (C) 2011 - 2015 W. Renxin. All Rights Reserved.\n"); + printf("For more information, see https://github.com/paladin-t/my_basic/.\n"); + printf("Input HELP and hint enter to view help information.\n"); +} + +static void _show_help(void) { + printf("Commands:\n"); + printf(" CLS - Clear screen\n"); + printf(" NEW - Clear current program\n"); + printf(" RUN - Run current program\n"); + printf(" BYE - Quit interpreter\n"); + printf(" LIST - List current program\n"); + printf(" Usage: LIST [l [n]], l is start line number, n is line count\n"); + printf(" EDIT - Edit a line in current program\n"); + printf(" Usage: EDIT n, n is line number\n"); + printf(" LOAD - Load a file as current program\n"); + printf(" Usage: LOAD *.*\n"); + printf(" SAVE - Save current program to a file\n"); + printf(" Usage: SAVE *.*\n"); + printf(" KILL - Delete a file\n"); + printf(" Usage: KILL *.*\n"); +} + +static int _do_line(void) { + int result = MB_FUNC_OK; + char line[_MAX_LINE_LENGTH]; + char dup[_MAX_LINE_LENGTH]; + + mb_assert(bas); + + memset(line, 0, _MAX_LINE_LENGTH); + printf("]"); + mb_gets(line, _MAX_LINE_LENGTH); + + memcpy(dup, line, _MAX_LINE_LENGTH); + strtok(line, " "); + + if(_str_eq(line, "")) { + /* Do nothing */ + } else if(_str_eq(line, "HELP")) { + _show_help(); + } else if(_str_eq(line, "CLS")) { + _clear_screen(); + } else if(_str_eq(line, "NEW")) { + result = _new_program(); + } else if(_str_eq(line, "RUN")) { + char* txt = _get_code(c); + result = mb_reset(&bas, false); + result = mb_load_string(bas, txt); + free(txt); + result = mb_run(bas); + printf("\n"); + } else if(_str_eq(line, "BYE")) { + result = MB_FUNC_BYE; + } else if(_str_eq(line, "LIST")) { + char* sn = line + strlen(line) + 1; + char* cn = 0; + strtok(sn, " "); + cn = sn + strlen(sn) + 1; + _list_program(sn, cn); + } else if(_str_eq(line, "EDIT")) { + char* no = line + strlen(line) + 1; + _edit_program(no); + } else if(_str_eq(line, "LOAD")) { + char* path = line + strlen(line) + 1; + _load_program(path); + } else if(_str_eq(line, "SAVE")) { + char* path = line + strlen(line) + 1; + _save_program(path); + } else if(_str_eq(line, "KILL")) { + char* path = line + strlen(line) + 1; + _kill_program(path); + } else { + _append_line(c, dup); + } + + return result; +} + +int main(int argc, char* argv[]) { + int status = 0; + +#if defined _MSC_VER && !defined _WIN64 + _CrtSetBreakAlloc(0); +#endif /* _MSC_VER && !_WIN64 */ + + atexit(_on_exit); + + _on_startup(); + + if(argc == 1) { + _show_tip(); + do { + status = _do_line(); + } while(_NO_END(status)); + } else if(argc == 2) { + if(mb_load_file(bas, argv[1]) == MB_FUNC_OK) { + mb_run(bas); + } + } else { + printf("Unknown arguments\n"); + _show_tip(); + } + + return 0; +}