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.

This commit is contained in:
tony 2015-04-10 22:04:41 +08:00
parent eb5cd615ce
commit e872d26513
7 changed files with 784 additions and 734 deletions

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -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 <crtdbg.h>
# include <conio.h>
#elif !defined __BORLANDC__
# include <unistd.h>
#endif /* _MSC_VER */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <crtdbg.h>
# include <conio.h>
#elif !defined __BORLANDC__
# include <unistd.h>
#endif /* _MSC_VER */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}