From 144d39285198222da8d55e612c51bf39be600a8f Mon Sep 17 00:00:00 2001 From: Wang Renxin Date: Mon, 26 Dec 2016 16:38:01 +0800 Subject: [PATCH] *unified line ending. --- .travis.yml | 14 +- core/my_basic.c | 34198 +++++++++++++++++++------------------- core/my_basic.h | 1304 +- resource/my_basic.rc | 154 +- sample/README.md | 80 +- sample/sample06.bas | 54 +- sample/sample07.bas | 200 +- sample/yard/README.md | 50 +- sample/yard/entity.bas | 94 +- sample/yard/goal.bas | 40 +- sample/yard/level.bas | 524 +- sample/yard/map.bas | 190 +- sample/yard/monster.bas | 24 +- sample/yard/npc.bas | 40 +- sample/yard/player.bas | 30 +- sample/yard/start.bas | 62 +- sample/yard/utils.bas | 184 +- 17 files changed, 18621 insertions(+), 18621 deletions(-) diff --git a/.travis.yml b/.travis.yml index 78ef162..9f1260e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ -language: c - -compiler: - - clang - - gcc - -script: make +language: c + +compiler: + - clang + - gcc + +script: make diff --git a/core/my_basic.c b/core/my_basic.c index a45e550..c578062 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -1,17099 +1,17099 @@ -/* -** This source file is part of MY-BASIC -** -** For the latest info, see https://github.com/paladin-t/my_basic/ -** -** Copyright (C) 2011 - 2016 Wang 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 "my_basic.h" -#if defined ARDUINO && !defined MB_CP_ARDUINO -# define MB_CP_ARDUINO -#endif /* ARDUINO && !MB_CP_ARDUINO */ -#ifdef MB_CP_VC -# include -# include -# include -# include -#else /* MB_CP_VC */ -# include -#endif /* MB_CP_VC */ -#ifndef MB_CP_ARDUINO -# include -#endif /* MB_CP_ARDUINO */ -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifdef MB_CP_VC -# pragma warning(push) -# pragma warning(disable : 4127) -# pragma warning(disable : 4305) -# pragma warning(disable : 4309) -# pragma warning(disable : 4805) -# pragma warning(disable : 4996) -#endif /* MB_CP_VC */ - -#ifdef MB_CP_CLANG -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-function" -# pragma clang diagnostic ignored "-Wunused-variable" -#endif /* MB_CP_CLANG */ - -#ifdef MB_CP_BORLANDC -# pragma warn -8004 -# pragma warn -8008 -# pragma warn -8012 -#endif /* MB_CP_BORLANDC */ - -#ifdef MB_COMPACT_MODE -# pragma pack(1) -#endif /* MB_COMPACT_MODE */ - -/* -** {======================================================== -** Data type declarations -*/ - -/** Macros */ - -/* Version information */ -#define _VER_MAJOR 1 -#define _VER_MINOR 2 -#define _VER_REVISION 0 -#define _VER_SUFFIX -#define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) -#define _STRINGIZE(A) _MAKE_STRINGIZE(A) -#define _MAKE_STRINGIZE(A) #A -#if _VER_REVISION == 0 -# define _MB_VERSION_STRING _STRINGIZE(_VER_MAJOR._VER_MINOR _VER_SUFFIX) -#else /* _VER_REVISION == 0 */ -# define _MB_VERSION_STRING _STRINGIZE(_VER_MAJOR._VER_MINOR._VER_REVISION _VER_SUFFIX) -#endif /* _VER_REVISION == 0 */ - -/* Define as 1 to create hash table nodes lazily, 0 obligingly */ -#define _LAZY_HASH_TABLE 1 - -/* Define as 1 to treat warning as error, 0 just leave it */ -#define _WARNING_AS_ERROR 0 - -/* Define as 1 to use a comma to PRINT a new line, 0 to use a semicolon */ -#define _COMMA_AS_NEWLINE 0 - -#define _NO_EAT_COMMA 2 - -/* Hash table size */ -#define _HT_ARRAY_SIZE_TINY 1 -#define _HT_ARRAY_SIZE_SMALL 193 -#define _HT_ARRAY_SIZE_MID 1543 -#define _HT_ARRAY_SIZE_BIG 12289 -#define _HT_ARRAY_SIZE_DEFAULT _HT_ARRAY_SIZE_SMALL - -/* Max length of a single symbol */ -#define _SINGLE_SYMBOL_MAX_LENGTH 128 - -/* Buffer length of some string operations */ -#define _INPUT_MAX_LENGTH 256 -#define _TEMP_FORMAT_MAX_LENGTH 32 -#define _LAMBDA_NAME_MAX_LENGTH 32 - -/* Helper */ -#ifndef sgn -# define sgn(__v) ((__v) ? ((__v) > 0 ? 1 : -1) : 0) -#endif /* sgn */ - -#ifndef countof -# define countof(__a) (sizeof(__a) / sizeof(*(__a))) -#endif /* countof */ - -#ifndef islower -# define islower(__c) ((__c) >= 'a' && (__c) <= 'z') -#endif /* islower */ - -#ifndef toupper -# define toupper(__c) (islower(__c) ? ((__c) - 'a' + 'A') : (__c)) -#endif /* toupper */ - -#ifndef _mb_unaligned -# if defined MB_CP_VC && defined MB_OS_WIN64 -# define _mb_unaligned __unaligned -# else -# define _mb_unaligned -# endif -#endif /* _mb_unaligned */ - -#define _COPY_BYTES(__l, __r) do { memcpy((__l), (__r), sizeof(mb_val_bytes_t)); } while(0) - -#define _mb_check(__expr, __exit) do { if((__expr) != MB_FUNC_OK) goto __exit; } while(0) -#define _mb_check_mark(__expr, __result, __exit) do { __result = (__expr); if(__result != MB_FUNC_OK) goto __exit; } while(0) - -#define DON(__o) ((__o) ? ((_object_t*)((__o)->data)) : 0) -#define DON2(__a) (((__a) && *(__a)) ? (_object_t*)((*((_ls_node_t**)(__a)))->data) : 0) -#define TON(__t) (((__t) && *(__t)) ? (_object_t*)(((_tuple3_t*)(*(__t)))->e1) : 0) - -#define _IS_VAR_ARGS(__v) ((__v) == &_VAR_ARGS) - -#define _IS_EOS(__o) (__o && ((_object_t*)(__o))->type == _DT_EOS) -#define _IS_SEP(__o, __c) (((_object_t*)(__o))->type == _DT_SEP && ((_object_t*)(__o))->data.separator == __c) -#define _IS_FUNC(__o, __f) (((_object_t*)(__o))->type == _DT_FUNC && ((_object_t*)(__o))->data.func->pointer == __f) -#define _IS_UNARY_FUNC(__o) (((_object_t*)(__o))->type == _DT_FUNC && _is_unary(((_object_t*)(__o))->data.func->pointer)) -#define _IS_VAR(__o) ((__o) && ((_object_t*)(__o))->type == _DT_VAR) - -/* Collection functors */ -#define _OP_RESULT_NORMAL 0 -#define _OP_RESULT_DEL_NODE -1 - -typedef int (* _common_compare)(void*, void*); -typedef int (* _common_operation)(void*, void*); - -/** List */ - -typedef _common_compare _ls_compare; -typedef _common_operation _ls_operation; - -typedef struct _ls_node_t { - void* data; - struct _ls_node_t* prev; - struct _ls_node_t* next; - void* extra; -} _ls_node_t; - -/** Dictionary */ - -typedef unsigned (* _ht_hash)(void*, void*); -typedef _common_compare _ht_compare; -typedef _common_operation _ht_operation; - -typedef struct _ht_node_t { - _ls_operation free_extra; - _ht_compare compare; - _ht_hash hash; - unsigned array_size; - unsigned count; - _ls_node_t** array; -} _ht_node_t; - -/** Normal enum/struct/union/const, etc. */ - -/* Error description text */ -static const char* _ERR_DESC[] = { - "No error", - /** Common */ - "Open MY-BASIC failed", - "A function with the same name already exists", - "A function with the name does not exists", - "Not supported", - /** Parsing */ - "Open file failed", - "Symbol too long", - "Invalid character", - /** Running */ - "Empty program", - "Program too long", - "Syntax error", - "Invalid data type", - "Type does not match", - "Number overflow", - "Invalid string", - "Index out of bound", - "Cannot find with given index", - "Illegal bound", - "Too many dimensions", - "Operation failed", - "Invalid operation usage", - "Dimension count out of bound", - "Rank out of bound", - "Complex array required", - "Label does not exist", - "No return point", - "Colon expected", - "Comma expected", - "Comma or semicolon expected", - "Array identifier expected", - "Open bracket expected", - "Close bracket expected", - "Array subscript expected", - "Nested too deep", - "Incomplete structure", - "Function expected", - "Variable or array identifier expected", - "Assign operator expected", - "String expected", - "Number expected", - "Integer expected", - "ELSE statement expected", - "TO statement expected", - "NEXT statement expected", - "UNTIL statement expected", - "Loop variable expected", - "Jump label expected", - "Variable expected", - "Invalid identifier usage", - "Duplicate identifier", - "Operator expected", - "Calculation error", - "Divide by zero", - "MOD by zero", - "Invalid expression", - "Out of memory", - "Module not match", - "Wrong function reached", - "Do not suspend in a routine", - "Do not mix instructional and structured sub routines", - "Invalid routine", - "Incomplete routine", - "Routine expected", - "Duplicate routine", - "To many routines", - "Invalid class", - "Incomplete class", - "Class expected", - "Duplicate class", - "Wrong meta class", - "HASH and COMPARE must come together", - "Cannot change ME", - "Invalid lambda", - "List expected", - "Collection expected", - "Iterator expected", - "Collection or iterator expected", - "Collection or iterator or class expected", - "Invalid iterator", - "Empty collection", - "Referenced type expected", - "Reference count overflow", - "Weak reference count overflow", - "Debug identifier not found", - "Stack trace disabled", - /** Extended abort */ - "Extended abort" -}; - -mb_static_assert(countof(_ERR_DESC) == SE_COUNT); - -/* Data type */ -typedef enum _data_e { - _DT_INVALID = -1, - _DT_NIL = 0, - _DT_UNKNOWN, - _DT_INT, - _DT_REAL, - _DT_STRING, - _DT_TYPE, - _DT_USERTYPE, -#ifdef MB_ENABLE_USERTYPE_REF - _DT_USERTYPE_REF, -#endif /* MB_ENABLE_USERTYPE_REF */ - _DT_FUNC, - _DT_VAR, - _DT_ARRAY, -#ifdef MB_ENABLE_COLLECTION_LIB - _DT_LIST, - _DT_LIST_IT, - _DT_DICT, - _DT_DICT_IT, -#endif /* MB_ENABLE_COLLECTION_LIB */ - _DT_LABEL, /* Label type, used for GOTO, GOSUB statement */ -#ifdef MB_ENABLE_CLASS - _DT_CLASS, /* Object instance */ -#endif /* MB_ENABLE_CLASS */ - _DT_ROUTINE, /* User defined sub routine in script */ -#ifdef MB_ENABLE_LAMBDA - _DT_OUTER_SCOPE, -#endif /* MB_ENABLE_LAMBDA */ - _DT_SEP, /* Separator */ -#ifdef MB_ENABLE_SOURCE_TRACE - _DT_PREV_IMPORT, - _DT_POST_IMPORT, -#endif /* MB_ENABLE_SOURCE_TRACE */ - _DT_EOS /* End of statement */ -} _data_e; - -#ifdef MB_ENABLE_COLLECTION_LIB -# define _HAS_REF_OBJ_LOCK -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifdef _HAS_REF_OBJ_LOCK -typedef short _lock_t; -#endif /* _HAS_REF_OBJ_LOCK */ - -struct _ref_t; - -typedef void (* _unref_func_t)(struct _ref_t*, void*); - -#define _NONE_REF 1 - -typedef unsigned _ref_count_t; - -/* The reference structure should be always at the head of an object */ -typedef struct _ref_t { - _ref_count_t* count; - _ref_count_t* weak_count; - _unref_func_t on_unref; - _data_e type; - struct mb_interpreter_t* s; -} _ref_t; - -typedef struct _gc_t { - _ht_node_t* table; - _ht_node_t* recursive_table; - _ht_node_t* collected_table; - _ht_node_t* valid_table; - int_t collecting; -} _gc_t; - -typedef struct _calculation_operator_info_t { - mb_meta_operator_t is; - mb_meta_operator_t add; - mb_meta_operator_t sub; - mb_meta_operator_t mul; - mb_meta_operator_t div; - mb_meta_operator_t neg; -} _calculation_operator_info_t; - -typedef struct _usertype_ref_t { - _ref_t ref; - void* usertype; - mb_dtor_func_t dtor; - mb_clone_func_t clone; - mb_hash_func_t hash; - mb_cmp_func_t cmp; - mb_fmt_func_t fmt; - _calculation_operator_info_t* calc_operators; - mb_meta_func_t coll_func; - mb_meta_func_t generic_func; -} _usertype_ref_t; - -typedef struct _func_t { - char* name; - mb_func_t pointer; -} _func_t; - -#define _PATHING_NONE 0 -#define _PATHING_NORMAL 1 -#define _PATHING_UNKNOWN_FOR_NOT_FOUND 2 - -#define _PN(__b) ((!!(__b)) ? (_PATHING_NORMAL) : (_PATHING_NONE)) -#define _PU(__b) ((!!(__b)) ? (_PATHING_UNKNOWN_FOR_NOT_FOUND) : (_PATHING_NONE)) - -typedef struct _var_t { - char* name; - struct _object_t* data; -#ifdef MB_ENABLE_CLASS - bool_t pathing; - bool_t isme; -#endif /* MB_ENABLE_CLASS */ -} _var_t; - -typedef struct _array_t { -#ifdef MB_ENABLE_ARRAY_REF - _ref_t ref; -#endif /* MB_ENABLE_ARRAY_REF */ - char* name; - _data_e type; -#ifndef MB_SIMPLE_ARRAY - _data_e* types; -#endif /* MB_SIMPLE_ARRAY */ - void* raw; - unsigned count; - unsigned char dimension_count; - unsigned dimensions[MB_MAX_DIMENSION_COUNT]; -} _array_t; - -#ifdef MB_ENABLE_COLLECTION_LIB -typedef struct _array_helper_t { - struct mb_interpreter_t* s; - _array_t* array; - int index; -} _array_helper_t; -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifdef MB_ENABLE_COLLECTION_LIB -typedef struct _list_t { - _ref_t ref; - _lock_t lock; - _ls_node_t* list; - _ls_node_t* cached_node; - int cached_index; - int_t count; - int_t* range_begin; -} _list_t; - -typedef struct _list_it_t { - _ref_t weak_ref; - _list_t* list; - bool_t locking; - union { - _ls_node_t* node; - int_t ranging; - } curr; -} _list_it_t; - -typedef struct _dict_t { - _ref_t ref; - _lock_t lock; - _ht_node_t* dict; -} _dict_t; - -#define _INVALID_DICT_IT ((_ls_node_t*)(intptr_t)~0) - -typedef struct _dict_it_t { - _ref_t weak_ref; - _dict_t* dict; - bool_t locking; - unsigned curr_bucket; - _ls_node_t* curr_node; -} _dict_it_t; -#endif /* MB_ENABLE_COLLECTION_LIB */ - -typedef struct _label_t { - char* name; - _ls_node_t* node; -} _label_t; - -#ifdef MB_ENABLE_CLASS -#define _META_LIST_MAX_DEPTH UINT_MAX - -#define _CLASS_ME "ME" - -#define _CLASS_HASH_FUNC "HASH" -#define _CLASS_COMPARE_FUNC "COMPARE" -#define _CLASS_TOSTRING_FUNC "TOSTRING" - -#define _CLASS_OVERRIDE_FMT "_%s" - -typedef struct _class_t { - _ref_t ref; - char* name; - struct _class_t* created_from; - _ls_node_t* meta_list; - struct _running_context_t* scope; - struct _routine_t* hash; - struct _routine_t* compare; - void* userdata; -} _class_t; -#endif /* MB_ENABLE_CLASS */ - -typedef enum _invokable_e { - _IT_SCRIPT = 1, -#ifdef MB_ENABLE_LAMBDA - _IT_LAMBDA, -#endif /* MB_ENABLE_LAMBDA */ - _IT_NATIVE -} _invokable_e; - -#ifdef MB_ENABLE_LAMBDA -typedef struct _running_context_ref_t { - _ref_t ref; - struct _running_context_ref_t* prev; - struct _running_context_t* scope; -} _running_context_ref_t; - -typedef struct _upvalue_scope_tuple_t { - struct mb_interpreter_t* s; -#ifdef MB_ENABLE_CLASS - _class_t* instance; -#endif /* MB_ENABLE_CLASS */ - struct _running_context_t* scope; - struct _running_context_ref_t* outer_scope; - struct _lambda_t* lambda; - _ht_node_t* filled; -} _upvalue_scope_tuple_t; - -typedef struct _lambda_t { - _ref_t ref; - struct _running_context_t* scope; - _ls_node_t* parameters; - struct _running_context_ref_t* outer_scope; - _ht_node_t* upvalues; - _ls_node_t* entry; - _ls_node_t* end; -} _lambda_t; -#endif /* MB_ENABLE_LAMBDA */ - -typedef struct _routine_t { - union { - struct { - struct _running_context_t* scope; - _ls_node_t* parameters; - _ls_node_t* entry; - } basic; -#ifdef MB_ENABLE_LAMBDA - _lambda_t lambda; -#endif /* MB_ENABLE_LAMBDA */ - struct { - mb_routine_func_t entry; - } native; - } func; - char* name; -#ifdef MB_ENABLE_SOURCE_TRACE - char* source_file; -#endif /* MB_ENABLE_SOURCE_TRACE */ -#ifdef MB_ENABLE_CLASS - _class_t* instance; -#endif /* MB_ENABLE_CLASS */ - bool_t is_cloned; - _invokable_e type; -} _routine_t; - -#ifdef MB_ENABLE_SOURCE_TRACE -typedef struct _import_info_t { - char* source_file; -} _import_info_t; -#endif /* MB_ENABLE_SOURCE_TRACE */ - -typedef union _raw_u { mb_data_e e; char c; int_t i; real_t r; void* p; mb_val_bytes_t b; } _raw_u; - -typedef unsigned char _raw_t[sizeof(_raw_u)]; - -typedef struct _object_t { - _data_e type; - union { - int_t integer; - real_t float_point; - char* string; - mb_data_e type; - void* usertype; - _usertype_ref_t* usertype_ref; - _func_t* func; - _var_t* variable; - _array_t* array; -#ifdef MB_ENABLE_COLLECTION_LIB - _list_t* list; - _list_it_t* list_it; - _dict_t* dict; - _dict_it_t* dict_it; -#endif /* MB_ENABLE_COLLECTION_LIB */ - _label_t* label; -#ifdef MB_ENABLE_CLASS - _class_t* instance; -#endif /* MB_ENABLE_CLASS */ - _routine_t* routine; - char separator; -#ifdef MB_ENABLE_SOURCE_TRACE - _import_info_t* import_info; -#endif /* MB_ENABLE_SOURCE_TRACE */ - mb_val_bytes_t bytes; - void* pointer; - _raw_t raw; - } data; - bool_t ref; -#ifdef MB_ENABLE_SOURCE_TRACE - int source_pos; - unsigned short source_row; - unsigned short source_col; -#else /* MB_ENABLE_SOURCE_TRACE */ - char source_pos; -#endif /* MB_ENABLE_SOURCE_TRACE */ -} _object_t; - -#ifdef MB_ENABLE_MODULE -typedef struct _module_func_t { - char* module; - mb_func_t func; -} _module_func_t; -#endif /* MB_ENABLE_MODULE */ - -typedef struct _dynamic_buffer_t { - char bytes[_TEMP_FORMAT_MAX_LENGTH]; - union { - char* charp; -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE - wchar_t* wcharp; -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ - } pointer; - size_t size; -} _dynamic_buffer_t; - -#define _MB_MEM_TAG_SIZE (sizeof(mb_mem_tag_t)) - -MBAPI size_t MB_SIZEOF_4BYTES = 4; -MBAPI size_t MB_SIZEOF_8BYTES = 8; -MBAPI size_t MB_SIZEOF_32BYTES = 32; -MBAPI size_t MB_SIZEOF_64BYTES = 64; -MBAPI size_t MB_SIZEOF_128BYTES = 128; -MBAPI size_t MB_SIZEOF_256BYTES = 256; -MBAPI size_t MB_SIZEOF_512BYTES = 512; -#ifdef MB_ENABLE_ALLOC_STAT -MBAPI size_t MB_SIZEOF_INT = _MB_MEM_TAG_SIZE + sizeof(int); -MBAPI size_t MB_SIZEOF_PTR = _MB_MEM_TAG_SIZE + sizeof(intptr_t); -MBAPI size_t MB_SIZEOF_LSN = _MB_MEM_TAG_SIZE + sizeof(_ls_node_t); -MBAPI size_t MB_SIZEOF_HTN = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t); -MBAPI size_t MB_SIZEOF_HTA = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT; -MBAPI size_t MB_SIZEOF_OBJ = _MB_MEM_TAG_SIZE + sizeof(_object_t); -#ifdef MB_ENABLE_USERTYPE_REF -MBAPI size_t MB_SIZEOF_UTR = _MB_MEM_TAG_SIZE + sizeof(_usertype_ref_t); -#endif /* MB_ENABLE_USERTYPE_REF */ -MBAPI size_t MB_SIZEOF_FUN = _MB_MEM_TAG_SIZE + sizeof(_func_t); -MBAPI size_t MB_SIZEOF_VAR = _MB_MEM_TAG_SIZE + sizeof(_var_t); -MBAPI size_t MB_SIZEOF_ARR = _MB_MEM_TAG_SIZE + sizeof(_array_t); -#ifdef MB_ENABLE_COLLECTION_LIB -MBAPI size_t MB_SIZEOF_LST = _MB_MEM_TAG_SIZE + sizeof(_list_t); -MBAPI size_t MB_SIZEOF_DCT = _MB_MEM_TAG_SIZE + sizeof(_dict_t); -#endif /* MB_ENABLE_COLLECTION_LIB */ -MBAPI size_t MB_SIZEOF_LBL = _MB_MEM_TAG_SIZE + sizeof(_label_t); -#ifdef MB_ENABLE_CLASS -MBAPI size_t MB_SIZEOF_CLS = _MB_MEM_TAG_SIZE + sizeof(_class_t); -#endif /* MB_ENABLE_CLASS */ -MBAPI size_t MB_SIZEOF_RTN = _MB_MEM_TAG_SIZE + sizeof(_routine_t); -#else /* MB_ENABLE_ALLOC_STAT */ -MBAPI size_t MB_SIZEOF_INT = sizeof(int); -MBAPI size_t MB_SIZEOF_PTR = sizeof(intptr_t); -MBAPI size_t MB_SIZEOF_LSN = sizeof(_ls_node_t); -MBAPI size_t MB_SIZEOF_HTN = sizeof(_ht_node_t); -MBAPI size_t MB_SIZEOF_HTA = sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT; -MBAPI size_t MB_SIZEOF_OBJ = sizeof(_object_t); -#ifdef MB_ENABLE_USERTYPE_REF -MBAPI size_t MB_SIZEOF_UTR = sizeof(_usertype_ref_t); -#endif /* MB_ENABLE_USERTYPE_REF */ -MBAPI size_t MB_SIZEOF_FUN = sizeof(_func_t); -MBAPI size_t MB_SIZEOF_VAR = sizeof(_var_t); -MBAPI size_t MB_SIZEOF_ARR = sizeof(_array_t); -#ifdef MB_ENABLE_COLLECTION_LIB -MBAPI size_t MB_SIZEOF_LST = sizeof(_list_t); -MBAPI size_t MB_SIZEOF_DCT = sizeof(_dict_t); -#endif /* MB_ENABLE_COLLECTION_LIB */ -MBAPI size_t MB_SIZEOF_LBL = sizeof(_label_t); -#ifdef MB_ENABLE_CLASS -MBAPI size_t MB_SIZEOF_CLS = sizeof(_class_t); -#endif /* MB_ENABLE_CLASS */ -MBAPI size_t MB_SIZEOF_RTN = sizeof(_routine_t); -#endif /* MB_ENABLE_ALLOC_STAT */ - -#ifdef MB_ENABLE_SOURCE_TRACE -static const _object_t _OBJ_INT_UNIT = { _DT_INT, (int_t)1, false, 0, 0, 0 }; -static const _object_t _OBJ_INT_ZERO = { _DT_INT, (int_t)0, false, 0, 0, 0 }; -#else /* MB_ENABLE_SOURCE_TRACE */ -static const _object_t _OBJ_INT_UNIT = { _DT_INT, (int_t)1, false, 0 }; -static const _object_t _OBJ_INT_ZERO = { _DT_INT, (int_t)0, false, 0 }; -#endif /* MB_ENABLE_SOURCE_TRACE */ -#define _MAKE_NIL(__o) do { memset((__o), 0, sizeof(_object_t)); (__o)->type = _DT_NIL; } while(0) - -static _object_t* _OBJ_BOOL_TRUE = 0; -static _object_t* _OBJ_BOOL_FALSE = 0; - -#ifdef MB_ENABLE_CLASS -#ifdef MB_ENABLE_SOURCE_TRACE -static const _object_t _OBJ_UNKNOWN = { _DT_UNKNOWN, (int_t)0, false, 0, 0, 0 }; -#else /* MB_ENABLE_SOURCE_TRACE */ -static const _object_t _OBJ_UNKNOWN = { _DT_UNKNOWN, (int_t)0, false, 0 }; -#endif /* MB_ENABLE_SOURCE_TRACE */ -static const _ls_node_t _LS_NODE_UNKNOWN = { (void*)&_OBJ_UNKNOWN, 0, 0, 0 }; -#endif /* MB_ENABLE_CLASS */ - -#define _VAR_ARGS_STR "..." - -#ifdef MB_ENABLE_CLASS -static const _var_t _VAR_ARGS = { _VAR_ARGS_STR, 0, 0, 0 }; -#else /* MB_ENABLE_CLASS */ -static const _var_t _VAR_ARGS = { _VAR_ARGS_STR, 0 }; -#endif /* MB_ENABLE_CLASS */ - -/* Parsing context */ -#define _CLASS_STATE_NONE 0 -#define _CLASS_STATE_PROC 1 - -static const char _MULTI_LINE_COMMENT_PREFIX[] = "'["; -static const char _MULTI_LINE_COMMENT_POSTFIX[] = "']"; - -typedef enum _parsing_state_e { - _PS_NORMAL, - _PS_STRING, - _PS_COMMENT, - _PS_MULTI_LINE_COMMENT -} _parsing_state_e; - -typedef enum _symbol_state_e { - _SS_IDENTIFIER, - _SS_OPERATOR -} _symbol_state_e; - -typedef struct _parsing_context_t { - _ls_node_t* imported; - char current_char; - char current_symbol[_SINGLE_SYMBOL_MAX_LENGTH + 1]; - int current_symbol_nonius; - int current_symbol_contains_accessor; - _object_t* last_symbol; - int multi_line_comment_count; - _parsing_state_e parsing_state; - _symbol_state_e symbol_state; -#ifdef MB_ENABLE_CLASS - unsigned short class_state; -#endif /* MB_ENABLE_CLASS */ - unsigned short routine_state; - unsigned short routine_params_state; - int parsing_pos; - unsigned short parsing_row; - unsigned short parsing_col; -} _parsing_context_t; - -/* Running context */ -#define _SCOPE_META_ROOT (1 << 0) -#define _SCOPE_META_REF (1 << 1) - -#define _INFINITY_CALC_DEPTH -1 - -typedef struct _running_context_t { - struct _running_context_t* prev; - unsigned meta; - _ht_node_t* var_dict; - struct _running_context_t* ref; - _var_t* next_loop_var; - mb_value_t intermediate_value; - int calc_depth; -#ifdef MB_ENABLE_LAMBDA - _ls_node_t* refered_lambdas; -#endif /* MB_ENABLE_LAMBDA */ -} _running_context_t; - -/* Expression processing utilities */ -typedef struct _tuple3_t { - void* e1; - void* e2; - void* e3; -} _tuple3_t; - -/* Interpreter tag */ -#define _JMP_NIL 0x00 -#define _JMP_INS 0x01 -#define _JMP_STR 0x02 - -typedef struct mb_interpreter_t { - /** Fundamental */ - bool_t valid; - void* userdata; - _ls_node_t* ast; - /** Memory management */ - _gc_t gc; - _ls_node_t* edge_destroy_objects; - _ls_node_t* lazy_destroy_objects; - /** Scripting interface and module */ - _ht_node_t* local_func_dict; - _ht_node_t* global_func_dict; -#ifdef MB_ENABLE_MODULE - _ht_node_t* module_func_dict; - char* with_module; - _ls_node_t* using_modules; -#endif /* MB_ENABLE_MODULE */ - /** Parsing and interpreting */ - char* source_file; - _parsing_context_t* parsing_context; - _running_context_t* running_context; - _ls_node_t* var_args; - unsigned char jump_set; -#ifdef MB_ENABLE_CLASS - _class_t* last_instance; - bool_t calling; -#endif /* MB_ENABLE_CLASS */ - _routine_t* last_routine; - _ls_node_t* sub_stack; - _ls_node_t* suspent_point; - int schedule_suspend_tag; - int_t no_eat_comma_mark; - _ls_node_t* skip_to_eoi; - _ls_node_t* in_neg_expr; -#ifdef MB_ENABLE_STACK_TRACE - _ls_node_t* stack_frames; -#endif /* MB_ENABLE_STACK_TRACE */ - /** Error handling */ - bool_t handled_error; - mb_error_e last_error; - char* last_error_file; - int last_error_pos; - unsigned short last_error_row; - unsigned short last_error_col; - /** Handlers */ - mb_debug_stepped_handler_t debug_stepped_handler; - mb_error_handler_t error_handler; - mb_print_func_t printer; - mb_input_func_t inputer; - mb_import_handler_t import_handler; -} mb_interpreter_t; - -/* Operations */ -static const char _PRECEDE_TABLE[20][20] = { /* Operator priority table */ - /* + - * / MOD ^ ( ) = > < >= <= == <> AND OR NOT NEG IS */ - { '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* + */ - { '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* - */ - { '>', '>', '>', '>', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* * */ - { '>', '>', '>', '>', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* / */ - { '>', '>', '<', '<', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* MOD */ - { '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* ^ */ - { '<', '<', '<', '<', '<', '<', '<', '=', ' ', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<' }, /* ( */ - { '>', '>', '>', '>', '>', '>', ' ', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* ) */ - { '<', '<', '<', '<', '<', '<', '<', ' ', '=', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<' }, /* = */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* > */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* < */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* >= */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* <= */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* == */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* <> */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' }, /* AND */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' }, /* OR */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>' }, /* NOT */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '<', '<', '<', '=', '<' }, /* NEG */ - { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' } /* IS */ -}; - -static _object_t* _exp_assign = 0; - -#define _set_real_with_hex(__r, __i) \ - do { \ - if(sizeof(__r) == sizeof(unsigned char)) { \ - unsigned char __b = __i; \ - memcpy(&(__r), &__b, sizeof(__r)); \ - } else if(sizeof(__r) == sizeof(unsigned short)) { \ - unsigned short __b = __i; \ - memcpy(&(__r), &__b, sizeof(__r)); \ - } else if(sizeof(__r) == sizeof(unsigned)) { \ - unsigned __b = __i; \ - memcpy(&(__r), &__b, sizeof(__r)); \ - } else if(sizeof(__r) == sizeof(unsigned long)) { \ - unsigned long __b = __i; \ - memcpy(&(__r), &__b, sizeof(__r)); \ - } else if(sizeof(__r) == sizeof(unsigned long long)) { \ - unsigned long long __b = __i; \ - memcpy(&(__r), &__b, sizeof(__r)); \ - } else { \ - mb_assert(0 && "Invalid real number precision."); \ - } \ - } while(0) - -#if MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE -# define _convert_to_int_if_posible(__o) do { ((void)(__o)); } while(0) -#else /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ -# define _convert_to_int_if_posible(__o) \ - do { \ - if((__o)->type == _DT_REAL && (real_t)(int_t)(__o)->data.float_point == (__o)->data.float_point) { \ - (__o)->type = _DT_INT; \ - (__o)->data.integer = (int_t)(__o)->data.float_point; \ - } \ - } while(0) -#endif /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ - -#define _instruct_head(__tuple) \ - _object_t opndv1; \ - _object_t opndv2; \ - _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ - _object_t* opnd1 = (_object_t*)(tpptr->e1); \ - _object_t* opnd2 = (_object_t*)(tpptr->e2); \ - _object_t* val = (_object_t*)(tpptr->e3); -#define _instruct_common(__tuple) \ - _instruct_head(__tuple) \ - opndv1.type = (opnd1->type == _DT_INT || (opnd1->type == _DT_VAR && opnd1->data.variable->data->type == _DT_INT)) ? \ - _DT_INT : _DT_REAL; \ - opndv1.data = opnd1->type == _DT_VAR ? opnd1->data.variable->data->data : opnd1->data; \ - opndv2.type = (opnd2->type == _DT_INT || (opnd2->type == _DT_VAR && opnd2->data.variable->data->type == _DT_INT)) ? \ - _DT_INT : _DT_REAL; \ - opndv2.data = opnd2->type == _DT_VAR ? opnd2->data.variable->data->data : opnd2->data; -#define _instruct_fun_num_num(__optr, __tuple) \ - do { \ - _instruct_common(__tuple) \ - if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t)__optr((real_t)opndv1.data.integer, (real_t)opndv2.data.integer); \ - } else { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t)__optr( \ - opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point, \ - opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point); \ - } \ - _convert_to_int_if_posible(val); \ - } while(0) -#define _instruct_bool_op_bool(__optr, __tuple) \ - do { \ - _instruct_common(__tuple) \ - if(opndv1.type == _DT_NIL) { opndv1.type = _DT_INT; opndv1.data.integer = 0; } \ - else if(opndv1.type != _DT_INT && opndv1.type != _DT_REAL) { opndv1.type = _DT_INT; opndv1.data.integer = 1; } \ - if(opndv2.type == _DT_NIL) { opndv2.type = _DT_INT; opndv2.data.integer = 0; } \ - else if(opndv2.type != _DT_INT && opndv2.type != _DT_REAL) { opndv2.type = _DT_INT; opndv2.data.integer = 1; } \ - if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ - if((real_t)(opndv1.data.integer __optr opndv2.data.integer) == (real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer) { \ - val->type = _DT_INT; \ - val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ - } else { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t)((real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer); \ - } \ - } else { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t) \ - ((opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point) __optr \ - (opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point)); \ - } \ - _convert_to_int_if_posible(val); \ - } while(0) -#define _instruct_int_op_int(__optr, __tuple) \ - do { \ - _instruct_common(__tuple) \ - if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ - val->type = _DT_INT; \ - val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ - } else { \ - val->type = _DT_INT; \ - val->data.integer = \ - ((opndv1.type == _DT_INT ? opndv1.data.integer : (int_t)(opndv1.data.float_point)) __optr \ - (opndv2.type == _DT_INT ? opndv2.data.integer : (int_t)(opndv2.data.float_point))); \ - } \ - } while(0) -#define _instruct_num_op_num(__optr, __tuple) \ - do { \ - _instruct_common(__tuple) \ - if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ - if((real_t)(opndv1.data.integer __optr opndv2.data.integer) == (real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer) { \ - val->type = _DT_INT; \ - val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ - } else { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t)((real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer); \ - } \ - } else { \ - val->type = _DT_REAL; \ - val->data.float_point = (real_t) \ - ((opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point) __optr \ - (opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point)); \ - } \ - _convert_to_int_if_posible(val); \ - } while(0) -#define _instruct_obj_op_obj(__optr, __tuple) \ - do { \ - _instruct_head(__tuple); \ - opndv1.type = opnd1->type == _DT_VAR ? opnd1->data.variable->data->type : opnd1->type; \ - opndv1.data = opnd1->type == _DT_VAR ? opnd1->data.variable->data->data : opnd1->data; \ - opndv2.type = opnd2->type == _DT_VAR ? opnd2->data.variable->data->type : opnd2->type; \ - opndv2.data = opnd2->type == _DT_VAR ? opnd2->data.variable->data->data : opnd2->data; \ - val->type = _DT_INT; \ - if(opndv1.type == opndv2.type) { \ - val->data.integer = (int_t)(mb_memcmp(&opndv1.data, &opndv2.data, sizeof(_raw_t)) __optr 0); \ - } else { \ - val->data.integer = (int_t)(opndv1.type __optr opndv2.type); \ - } \ - } while(0) -#define _instruct_connect_strings(__tuple) \ - do { \ - char* _str1 = 0; \ - char* _str2 = 0; \ - _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ - _object_t* opnd1 = (_object_t*)(tpptr->e1); \ - _object_t* opnd2 = (_object_t*)(tpptr->e2); \ - _object_t* val = (_object_t*)(tpptr->e3); \ - val->type = _DT_STRING; \ - if(val->data.string) { \ - safe_free(val->data.string); \ - } \ - _str1 = _extract_string(opnd1); \ - _str2 = _extract_string(opnd2); \ - val->data.string = (char*)mb_malloc(strlen(_str1) + strlen(_str2) + 1); \ - memset(val->data.string, 0, strlen(_str1) + strlen(_str2) + 1); \ - strcat(val->data.string, _str1); \ - strcat(val->data.string, _str2); \ - } while(0) -#define _instruct_compare_strings(__optr, __tuple) \ - do { \ - char* _str1 = 0; \ - char* _str2 = 0; \ - _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ - _object_t* opnd1 = (_object_t*)(tpptr->e1); \ - _object_t* opnd2 = (_object_t*)(tpptr->e2); \ - _object_t* val = (_object_t*)(tpptr->e3); \ - val->type = _DT_INT; \ - _str1 = _extract_string(opnd1); \ - _str2 = _extract_string(opnd2); \ - val->data.integer = strcmp(_str1, _str2) __optr 0; \ - } while(0) -#ifdef MB_ENABLE_USERTYPE_REF -# define _instruct_obj_meta_obj(__optr, __tuple, __result, __exit) \ - do { \ - _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ - _object_t* opnd1 = (_object_t*)(tpptr->e1); \ - _object_t* opnd2 = (_object_t*)(tpptr->e2); \ - _object_t* retval = (_object_t*)(tpptr->e3); \ - if(opnd1->type == _DT_VAR) opnd1 = opnd1->data.variable->data; \ - if(opnd2->type == _DT_VAR) opnd2 = opnd2->data.variable->data; \ - { \ - mb_value_t vfst, vscd; \ - mb_value_t ret; \ - mb_make_nil(vfst); \ - mb_make_nil(vscd); \ - mb_make_nil(ret); \ - if(opnd1->type == _DT_USERTYPE_REF && opnd1->data.usertype_ref->calc_operators && opnd1->data.usertype_ref->calc_operators->__optr) { \ - _internal_object_to_public_value(opnd1, &vfst); \ - _internal_object_to_public_value(opnd2, &vscd); \ - __result = opnd1->data.usertype_ref->calc_operators->__optr(s, (__tuple), &vfst, &vscd, &ret); \ - _public_value_to_internal_object(&ret, retval); \ - goto __exit; \ - } else if(opnd2->type == _DT_USERTYPE_REF && opnd2->data.usertype_ref->calc_operators && opnd2->data.usertype_ref->calc_operators->__optr) { \ - _internal_object_to_public_value(opnd1, &vfst); \ - _internal_object_to_public_value(opnd2, &vscd); \ - __result = opnd2->data.usertype_ref->calc_operators->__optr(s, (__tuple), &vfst, &vscd, &ret); \ - _public_value_to_internal_object(&ret, retval); \ - goto __exit; \ - } \ - } \ - } while(0) -#else /* MB_ENABLE_USERTYPE_REF */ -# define _instruct_obj_meta_obj(__optr, __tuple, __result, __exit) do { ((void)(__tuple)); ((void)(__result)); } while(0) -#endif /* MB_ENABLE_USERTYPE_REF */ -#define _proc_div_by_zero(__s, __tuple, __exit, __result, __kind) \ - do { \ - _instruct_common(__tuple) \ - if((opndv2.type == _DT_INT && opndv2.data.integer == 0) || (opndv2.type == _DT_REAL && opndv2.data.float_point == 0.0f)) { \ - if((opndv1.type == _DT_INT && opndv1.data.integer == 0) || (opndv1.type == _DT_REAL && opndv1.data.float_point == 0.0f)) { \ - val->type = _DT_REAL; \ - _set_real_with_hex(val->data.float_point, MB_FNAN); \ - } else { \ - val->type = _DT_REAL; \ - _set_real_with_hex(val->data.float_point, MB_FINF); \ - } \ - _handle_error_on_obj((__s), __kind, (__s)->source_file, ((__tuple) && *(__tuple)) ? ((_object_t*)(((_tuple3_t*)(*(__tuple)))->e1)) : 0, MB_FUNC_WARNING, __exit, __result); \ - } \ - } while(0) - -#define _set_tuple3_result(__l, __r) \ - do { \ - _object_t* val = (_object_t*)(((_tuple3_t*)(*(__l)))->e3); \ - val->type = _DT_INT; \ - val->data.integer = __r; \ - } while(0) - -#define _math_calculate_fun_real(__s, __l, __a, __f, __exit, __result) \ - do { \ - switch((__a).type) { \ - case MB_DT_INT: \ - (__a).value.float_point = (real_t)__f((real_t)(__a).value.integer); \ - (__a).type = MB_DT_REAL; \ - break; \ - case MB_DT_REAL: \ - (__a).value.float_point = (real_t)__f((__a).value.float_point); \ - break; \ - default: \ - _handle_error_on_obj(__s, SE_RN_NUMBER_EXPECTED, (__s)->source_file, ((__l) && *(__l)) ? ((_object_t*)(((_tuple3_t*)(*(__l)))->e1)) : 0, MB_FUNC_WARNING, __exit, __result); \ - break; \ - } \ - mb_convert_to_int_if_posible(__a); \ - } while(0) - -#define _using_jump_set_of_instructional(__s, __obj, __exit, __result) \ - do { \ - if((__s)->jump_set & (~_JMP_INS)) { \ - _handle_error_on_obj(__s, SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, (__s)->source_file, DON(__obj), MB_FUNC_ERR, __exit, __result); \ - } else { \ - (__s)->jump_set |= _JMP_INS; \ - } \ - } while(0) -#define _using_jump_set_of_structured(__s, __obj, __exit, __result) \ - do { \ - if((__s)->jump_set & (~_JMP_STR)) { \ - _handle_error_on_obj(__s, SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, (__s)->source_file, DON(__obj), MB_FUNC_ERR, __exit, __result); \ - } else { \ - (__s)->jump_set |= _JMP_STR; \ - } \ - } while(0) - -/* ========================================================} */ - -/* -** {======================================================== -** Private function declarations -*/ - -/** List operations */ - -static int _ls_cmp_data(void* node, void* info); -static int _ls_cmp_extra(void* node, void* info); -static int _ls_cmp_extra_object(void* node, void* info); -static int _ls_cmp_extra_string(void* node, void* info); -#ifdef MB_ENABLE_MODULE -static int _ls_cmp_module_func(void* node, void* info); -#endif /* MB_ENABLE_MODULE */ - -static _ls_node_t* _ls_create_node(void* data); -static _ls_node_t* _ls_create(void); -static _ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp, int* idx); -static _ls_node_t* _ls_back(_ls_node_t* node); -static _ls_node_t* _ls_pushback(_ls_node_t* list, void* data); -static void* _ls_popback(_ls_node_t* list); -static _ls_node_t* _ls_front(_ls_node_t* node); -static void* _ls_popfront(_ls_node_t* list); -static _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data); -static unsigned _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op); -static unsigned _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op); -static unsigned _ls_foreach(_ls_node_t* list, _ls_operation op); -static _ls_node_t* _ls_sort(_ls_node_t* _mb_unaligned * list, _ls_compare cmp); -static unsigned _ls_count(_ls_node_t* list); -static bool_t _ls_empty(_ls_node_t* list); -static void _ls_clear(_ls_node_t* list); -static void _ls_destroy(_ls_node_t* list); -static int _ls_free_extra(void* data, void* extra); -#define _LS_FOREACH(L, O, P, E) \ - do { \ - _ls_node_t* __lst = L; \ - int __opresult = _OP_RESULT_NORMAL; \ - _ls_node_t* __tmp = 0; \ - mb_assert(L); \ - __lst = __lst->next; \ - while(__lst) { \ - if(P != 0) { \ - P(__lst->data, __lst->extra, E); \ - } \ - if(O != 0) { \ - __opresult = O(__lst->data, __lst->extra); \ - } \ - __tmp = __lst; \ - __lst = __lst->next; \ - if(_OP_RESULT_DEL_NODE == __opresult) { \ - __tmp->prev->next = __lst; \ - if(__lst) { \ - __lst->prev = __tmp->prev; \ - } \ - safe_free(__tmp); \ - (L)->data = (char*)(L)->data - 1; \ - } \ - } \ - } while(0) - -/** Dictionary operations */ - -static unsigned _ht_hash_object(void* ht, void* d); -static unsigned _ht_hash_string(void* ht, void* d); -static unsigned _ht_hash_intptr(void* ht, void* d); -static unsigned _ht_hash_ref(void* ht, void* d); - -static int _ht_cmp_object(void* d1, void* d2); -static int _ht_cmp_string(void* d1, void* d2); -static int _ht_cmp_intptr(void* d1, void* d2); -static int _ht_cmp_ref(void* d1, void* d2); - -static _ht_node_t* _ht_create(unsigned size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra); -static _ls_node_t* _ht_find(_ht_node_t* ht, void* key); -static unsigned _ht_set_or_insert(_ht_node_t* ht, void* key, void* value); -static unsigned _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp); -static unsigned _ht_foreach(_ht_node_t* ht, _ht_operation op); -static unsigned _ht_count(_ht_node_t* ht); -static void _ht_clear(_ht_node_t* ht); -static void _ht_destroy(_ht_node_t* ht); -static int _ht_remove_exist(void* data, void* extra, _ht_node_t* ht); -#define _HT_FOREACH(H, O, P, E) \ - do { \ - _ls_node_t* __bucket = 0; \ - unsigned __ul = 0; \ - if((H)->array) { \ - for(__ul = 0; __ul < (H)->array_size; ++__ul) { \ - __bucket = (H)->array[__ul]; \ - if(__bucket) { \ - _LS_FOREACH(__bucket, O, P, E); \ - } \ - } \ - } \ - } while(0) - -/** Memory manipulations */ - -static void _init_dynamic_buffer(_dynamic_buffer_t* buf); -static void _dispose_dynamic_buffer(_dynamic_buffer_t* buf); -static size_t _countof_dynamic_buffer(_dynamic_buffer_t* buf, size_t es); -static void _resize_dynamic_buffer(_dynamic_buffer_t* buf, size_t es, size_t c); - -#define _INIT_BUF(b) do { _init_dynamic_buffer(&(b)); } while(0) -#define _DISPOSE_BUF(b) do { _dispose_dynamic_buffer(&(b)); } while(0) -#define _CHARS_OF_BUF(b) (_countof_dynamic_buffer((&b), sizeof(char))) -#define _RESIZE_CHAR_BUF(b, c) do { _resize_dynamic_buffer(&(b), sizeof(char), (c)); } while(0) -#define _HEAP_CHAR_BUF(b) (((b).pointer.charp != (b).bytes) ? ((b).pointer.charp) : (mb_memdup((b).pointer.charp, (unsigned)(b).size))) -#define _CHAR_BUF_PTR(b) ((b).pointer.charp) -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE -#define _WCHARS_OF_BUF(b) (_countof_dynamic_buffer((&b), sizeof(wchar_t))) -#define _RESIZE_WCHAR_BUF(b, c) do { _resize_dynamic_buffer(&(b), sizeof(wchar_t), (c)); } while(0) -#define _WCHAR_BUF_PTR(b) ((b).pointer.wcharp) -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ - -#define _MB_CHECK_MEM_TAG_SIZE(y, s) ((mb_mem_tag_t)(s) == (s)) -#define _MB_WRITE_MEM_TAG_SIZE(t, s) (*((mb_mem_tag_t*)((char*)(t) - _MB_MEM_TAG_SIZE)) = (mb_mem_tag_t)(s)) -#define _MB_READ_MEM_TAG_SIZE(t) (*((mb_mem_tag_t*)((char*)(t) - _MB_MEM_TAG_SIZE))) - -#ifdef MB_ENABLE_ALLOC_STAT -static volatile size_t _mb_allocated = 0; -#else /* MB_ENABLE_ALLOC_STAT */ -static const size_t _mb_allocated = (size_t)(~0); -#endif /* MB_ENABLE_ALLOC_STAT */ - -static mb_memory_allocate_func_t _mb_allocate_func = 0; -static mb_memory_free_func_t _mb_free_func = 0; - -static void* mb_malloc(size_t s); -static void mb_free(void* p); - -static int mb_memcmp(void* l, void* r, size_t s); -static size_t mb_memtest(void* p, size_t s); - -static char* mb_strdup(const char* 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) - -static bool_t mb_is_little_endian(void); - -/** Unicode handling */ - -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE -static int mb_bytes_to_wchar(const char* sz, wchar_t** out, size_t size); -static int mb_bytes_to_wchar_ansi(const char* sz, wchar_t** out, size_t size); -static int mb_wchar_to_bytes(const wchar_t* sz, char** out, size_t size); -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ - -static int mb_uu_getbom(const char** ch); -#ifdef MB_ENABLE_UNICODE -static int mb_uu_ischar(const char* ch); -static int mb_uu_strlen(const char* ch); -static int mb_uu_substr(const char* ch, int begin, int count, char** o); -#endif /* MB_ENABLE_UNICODE */ - -/** Expression processing */ - -static bool_t _is_operator(mb_func_t op); -static bool_t _is_flow(mb_func_t op); -static bool_t _is_unary(mb_func_t op); -static bool_t _is_binary(mb_func_t op); -static char _get_priority(mb_func_t op1, mb_func_t op2); -static int _get_priority_index(mb_func_t op); -static _object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status); -static bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj); -static bool_t _is_unexpected_calc_type(mb_interpreter_t* s, _object_t* obj); -static bool_t _is_referenced_calc_type(mb_interpreter_t* s, _object_t* obj); -static int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val); -static _ls_node_t* _push_var_args(mb_interpreter_t* s); -static void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args); -static int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg); -static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args); -static int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); -static int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); -#ifdef MB_ENABLE_LAMBDA -static int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); -#endif /* MB_ENABLE_LAMBDA */ -static int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); -static int _has_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r); -static int _pop_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val); -static int _has_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r); -static int _pop_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val); -static bool_t _is_print_terminal(mb_interpreter_t* s, _object_t* obj); -static mb_meta_status_u _try_overridden(mb_interpreter_t* s, void** l, mb_value_t* d, const char* f, mb_meta_func_u t); - -/** Handlers */ - -#define _handle_error_now(__s, __err, __f, __result) \ - do { \ - _set_current_error((__s), (__err), (__f)); \ - if((__s)->error_handler) { \ - if((__s)->handled_error) \ - break; \ - (__s)->handled_error = true; \ - ((__s)->error_handler)((__s), (__s)->last_error, (char*)mb_get_error_desc((__s)->last_error), \ - (__s)->last_error_file, \ - (__s)->parsing_context ? (__s)->parsing_context->parsing_pos : (__s)->last_error_pos, \ - (__s)->parsing_context ? (__s)->parsing_context->parsing_row : (__s)->last_error_row, \ - (__s)->parsing_context ? (__s)->parsing_context->parsing_col : (__s)->last_error_col, \ - (__result)); \ - } \ - } while(0) -#if _WARNING_AS_ERROR -# define _handle_error_at_pos(__s, __err, __f, __pos, __row, __col, __ret, __exit, __result) \ - do { \ - _set_current_error((__s), (__err), (__f)); \ - _set_error_pos((__s), (__pos), (__row), (__col)); \ - __result = (__ret); \ - goto __exit; \ - } while(0) -#else /* _WARNING_AS_ERROR */ -# define _handle_error_at_pos(__s, __err, __f, __pos, __row, __col, __ret, __exit, __result) \ - do { \ - _set_current_error((__s), (__err), (__f)); \ - _set_error_pos((__s), (__pos), (__row), (__col)); \ - if((__ret) != MB_FUNC_WARNING) { \ - __result = (__ret); \ - } \ - goto __exit; \ - } while(0) -#endif /* _WARNING_AS_ERROR */ -#ifdef MB_ENABLE_SOURCE_TRACE -# define _handle_error_on_obj(__s, __err, __f, __obj, __ret, __exit, __result) \ - do { \ - if(__obj) { \ - _handle_error_at_pos((__s), (__err), (__f), (__obj)->source_pos, (__obj)->source_row, (__obj)->source_col, (__ret), __exit, (__result)); \ - } else { \ - _handle_error_at_pos((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result)); \ - } \ - } while(0) -#else /* MB_ENABLE_SOURCE_TRACE */ -# define _handle_error_on_obj(__s, __err, __f, __obj, __ret, __exit, __result) \ - do { ((void)(__obj)); _handle_error_at_pos((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result)); } while(0) -#endif /* MB_ENABLE_SOURCE_TRACE */ - -#define _OUTTER_SCOPE(__s) ((__s)->prev ? (__s)->prev : (__s)) - -static void _set_current_error(mb_interpreter_t* s, mb_error_e err, char* f); - -static mb_print_func_t _get_printer(mb_interpreter_t* s); -static mb_input_func_t _get_inputer(mb_interpreter_t* s); - -static void _print_string(mb_interpreter_t* s, _object_t* obj); - -/** Parsing helpers */ - -static char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix); -static void _end_of_file(_parsing_context_t* context); - -#define _ZERO_CHAR '\0' -#define _NEWLINE_CHAR '\n' -#define _RETURN_CHAR '\r' -#define _STRING_POSTFIX_CHAR '$' -#define _DUMMY_ASSIGN_CHAR "#" - -#define _REMARK_STR "REM" - -static bool_t _is_blank_char(char c); -static bool_t _is_eof_char(char c); -static bool_t _is_newline_char(char c); -static bool_t _is_separator_char(char c); -static bool_t _is_bracket_char(char c); -static bool_t _is_quotation_char(char c); -static bool_t _is_comment_char(char c); -static bool_t _is_accessor_char(char c); -static bool_t _is_numeric_char(char c); -static bool_t _is_identifier_char(char c); -static bool_t _is_operator_char(char c); -static bool_t _is_exponential_char(char c); -static bool_t _is_using_char(char c); -static bool_t _is_exponent_prefix(char* s, int begin, int end); - -static int _append_char_to_symbol(mb_interpreter_t* s, char c); -#ifdef MB_ENABLE_UNICODE_ID -static int _append_uu_char_to_symbol(mb_interpreter_t* s, const char* str, int n); -#endif /* MB_ENABLE_UNICODE_ID */ -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, _raw_t* value); -static int _parse_char(mb_interpreter_t* s, const char* str, int n, 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); -static char* _prev_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col); -static char* _post_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col); - -/** Object processors */ - -static int_t _get_size_of(_data_e type); -static bool_t _try_get_value(_object_t* obj, mb_value_u* val, _data_e expected); - -static bool_t _is_number(void* obj); -static bool_t _is_string(void* obj); -static char* _extract_string(_object_t* obj); - -#ifdef MB_ENABLE_COLLECTION_LIB -# define _IS_LIST(__o) ((__o) && ((_object_t*)(__o))->type == _DT_LIST) -# define _IS_DICT(__o) ((__o) && ((_object_t*)(__o))->type == _DT_DICT) -# define _IS_COLL(__o) (_IS_LIST(__o) || _IS_DICT(__o)) -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS -# define _IS_CLASS(__o) ((__o) && ((_object_t*)(__o))->type == _DT_CLASS) -# define _GET_CLASS(__o) ((!__o) ? 0 : (_IS_CLASS(__o) ? (__o) : (_IS_VAR(__o) && _IS_CLASS((__o)->data.variable->data) ? (__o)->data.variable->data : 0))) -# define _IS_ME(__v) (!!(__v)->isme) -#else /* MB_ENABLE_CLASS */ -# define _IS_ME(__v) false -#endif /* MB_ENABLE_CLASS */ -#define _IS_ROUTINE(__o) ((__o) && ((_object_t*)(__o))->type == _DT_ROUTINE) -#define _GET_ROUTINE(__o) ((!__o) ? 0 : (_IS_ROUTINE(__o) ? (__o) : (_IS_VAR(__o) && _IS_ROUTINE((__o)->data.variable->data) ? (__o)->data.variable->data : 0))) - -#ifdef MB_ENABLE_USERTYPE_REF -# define _REF_USERTYPE_REF(__o) \ - case _DT_USERTYPE_REF: \ - _ref(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref); \ - break; -# define _UNREF_USERTYPE_REF(__o) \ - case _DT_USERTYPE_REF: \ - _unref(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref); \ - break; -# define _ADDGC_USERTYPE_REF(__o, __g) \ - case _DT_USERTYPE_REF: \ - _gc_add(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref, (__g)); \ - break; -#else /* MB_ENABLE_USERTYPE_REF */ -# define _REF_USERTYPE_REF(__o) { (void)(__o); } -# define _UNREF_USERTYPE_REF(__o) { (void)(__o); } -# define _ADDGC_USERTYPE_REF(__o, __g) { (void)(__o); (void)(__g); } -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_ARRAY_REF -# define _REF_ARRAY(__o) \ - case _DT_ARRAY: \ - if(!(__o)->ref) \ - _ref(&(__o)->data.array->ref, (__o)->data.array); \ - break; -# define _UNREF_ARRAY(__o) \ - case _DT_ARRAY: \ - if(!(__o)->ref) \ - _unref(&(__o)->data.array->ref, (__o)->data.array); \ - break; -# define _ADDGC_ARRAY(__o, __g) \ - case _DT_ARRAY: \ - if(!(__o)->ref) \ - _gc_add(&(__o)->data.array->ref, (__o)->data.array, (__g)); \ - break; -#else /* MB_ENABLE_ARRAY_REF */ -# define _REF_ARRAY(__o) { (void)(__o); } -# define _UNREF_ARRAY(__o) { (void)(__o); } -# define _ADDGC_ARRAY(__o, __g) { (void)(__o); (void)(__g); } -#endif /* MB_ENABLE_ARRAY_REF */ -#ifdef MB_ENABLE_COLLECTION_LIB -# define _REF_COLL(__o) \ - case _DT_LIST: \ - _ref(&(__o)->data.list->ref, (__o)->data.list); \ - break; \ - case _DT_DICT: \ - _ref(&(__o)->data.dict->ref, (__o)->data.dict); \ - break; -# define _UNREF_COLL(__o) \ - case _DT_LIST: \ - _unref(&(__o)->data.list->ref, (__o)->data.list); \ - break; \ - case _DT_DICT: \ - _unref(&(__o)->data.dict->ref, (__o)->data.dict); \ - break; -# define _ADDGC_COLL(__o, __g) \ - case _DT_LIST: \ - _gc_add(&(__o)->data.list->ref, (__o)->data.list, (__g)); \ - break; \ - case _DT_DICT: \ - _gc_add(&(__o)->data.dict->ref, (__o)->data.dict, (__g)); \ - break; -# define _UNREF_COLL_IT(__o) \ - case _DT_LIST_IT: \ - _destroy_list_it(obj->data.list_it); \ - break; \ - case _DT_DICT_IT: \ - _destroy_dict_it(obj->data.dict_it); \ - break; -# define _ADDGC_COLL_IT(__o, __g) \ - case _DT_LIST_IT: \ - _destroy_list_it(obj->data.list_it); \ - break; \ - case _DT_DICT_IT: \ - _destroy_dict_it(obj->data.dict_it); \ - break; -#else /* MB_ENABLE_COLLECTION_LIB */ -# define _REF_COLL(__o) { (void)(__o); } -# define _UNREF_COLL(__o) { (void)(__o); } -# define _ADDGC_COLL(__o, __g) { (void)(__o); (void)(__g); } -# define _UNREF_COLL_IT(__o) { (void)(__o); } -# define _ADDGC_COLL_IT(__o, __g) { (void)(__o); (void)(__g); } -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS -# define _REF_CLASS(__o) \ - case _DT_CLASS: \ - _ref(&(__o)->data.instance->ref, (__o)->data.instance); \ - break; -# define _UNREF_CLASS(__o) \ - case _DT_CLASS: \ - if(!(__o)->ref) \ - _unref(&(__o)->data.instance->ref, (__o)->data.instance); \ - break; -# define _ADDGC_CLASS(__o, __g) \ - case _DT_CLASS: \ - if(!(__o)->ref) \ - _gc_add(&(__o)->data.instance->ref, (__o)->data.instance, (__g)); \ - break; -#else /* MB_ENABLE_CLASS */ -# define _REF_CLASS(__o) { (void)(__o); } -# define _UNREF_CLASS(__o) { (void)(__o); } -# define _ADDGC_CLASS(__o, __g) { (void)(__o); (void)(__g); } -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_LAMBDA -# define _REF_ROUTINE(__o) \ - case _DT_ROUTINE: \ - _ref(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine); \ - break; -# define _UNREF_ROUTINE(__o) \ - case _DT_ROUTINE: \ - if(!(__o)->ref && (__o)->data.routine->type == _IT_LAMBDA) \ - _unref(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine); \ - else if(!(__o)->ref && (__o)->data.routine->type != _IT_LAMBDA) \ - _destroy_routine(0, (__o)->data.routine); \ - break; -# define _ADDGC_ROUTINE(__o, __g) \ - case _DT_ROUTINE: \ - if(!(__o)->ref && (__o)->data.routine->type == _IT_LAMBDA) \ - _gc_add(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine, (__g)); \ - else if(!(__o)->ref && (__o)->data.routine->type != _IT_LAMBDA) \ - _dispose_object(__o); \ - break; -#else /* MB_ENABLE_LAMBDA */ -# define _REF_ROUTINE(__o) { (void)(__o); } -# define _UNREF_ROUTINE(__o) { (void)(__o); } -# define _ADDGC_ROUTINE(__o, __g) \ - case _DT_ROUTINE: \ - ((void)(__g)); \ - _dispose_object(__o); \ - break; -#endif /* MB_ENABLE_LAMBDA */ -#define _ADDGC_STRING(__o) \ - case _DT_STRING: \ - _dispose_object(__o); \ - break; -#define _REF(__o) \ - switch((__o)->type) { \ - _REF_USERTYPE_REF(__o) \ - _REF_ARRAY(__o) \ - _REF_COLL(__o) \ - _REF_CLASS(__o) \ - _REF_ROUTINE(__o) \ - default: break; \ - } -#define _UNREF(__o) \ - switch((__o)->type) { \ - _UNREF_USERTYPE_REF(__o) \ - _UNREF_ARRAY(__o) \ - _UNREF_COLL(__o) \ - _UNREF_CLASS(__o) \ - _UNREF_ROUTINE(__o) \ - default: break; \ - } -#define _ADDGC(__o, __g) \ - if(!(__o)->data.pointer || !_ht_find((__g)->collected_table, (__o)->data.pointer)) { \ - switch((__o)->type) { \ - _ADDGC_USERTYPE_REF(__o, __g) \ - _ADDGC_ARRAY(__o, __g) \ - _ADDGC_COLL(__o, __g) \ - _ADDGC_COLL_IT(__o, __g) \ - _ADDGC_CLASS(__o, __g) \ - _ADDGC_ROUTINE(__o, __g) \ - _ADDGC_STRING(__o) \ - default: break; \ - } \ - } - -#ifdef _HAS_REF_OBJ_LOCK -static bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); -static bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); -static bool_t _write_on_ref_object(_lock_t* lk, _ref_t* ref, void* obj); -#endif /* _HAS_REF_OBJ_LOCK */ - -static _ref_count_t _ref(_ref_t* ref, void* data); -static bool_t _unref(_ref_t* ref, void* data); -static _ref_count_t _weak_ref(_ref_t* ref, void* data, _ref_t* weak); -static bool_t _weak_unref(_ref_t* weak); -static void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpreter_t* s); -static void _destroy_ref(_ref_t* ref); - -static void _gc_add(_ref_t* ref, void* data, _gc_t* gc); -static void _gc_remove(_ref_t* ref, void* data, _gc_t* gc); -static int _gc_add_reachable(void* data, void* extra, void* h); -static int _gc_add_reachable_both(void* data, void* extra, void* h); -static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht); -static int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc); -static int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc); -#ifdef MB_ENABLE_CLASS -static int _gc_destroy_garbage_in_class(void* data, void* extra, _gc_t* gc); -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_LAMBDA -static int _gc_destroy_garbage_in_lambda(void* data, void* extra, _gc_t* gc); -static void _gc_destroy_garbage_in_outer_scope(_running_context_ref_t* p, _gc_t* gc); -#endif /* MB_ENABLE_LAMBDA */ -static int _gc_destroy_garbage(void* data, void* extra, _gc_t* gc); -#ifdef MB_ENABLE_CLASS -static int _gc_destroy_garbage_class(void* data, void* extra, _gc_t* gc); -#endif /* MB_ENABLE_CLASS */ -static void _gc_swap_tables(mb_interpreter_t* s); -static void _gc_try_trigger(mb_interpreter_t* s); -static void _gc_collect_garbage(mb_interpreter_t* s, int depth); - -#ifdef MB_ENABLE_USERTYPE_REF -static _usertype_ref_t* _create_usertype_ref(mb_interpreter_t* s, void* val, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft); -static void _destroy_usertype_ref(_usertype_ref_t* c); -static void _unref_usertype_ref(_ref_t* ref, void* data); -#endif /* MB_ENABLE_USERTYPE_REF */ - -static _array_t* _create_array(mb_interpreter_t* s, const char* n, _data_e t); -#ifdef MB_ENABLE_ARRAY_REF -static void _destroy_array(_array_t* arr); -#endif /* MB_ENABLE_ARRAY_REF */ -static void _init_array(_array_t* arr); -static _array_t* _clone_array(mb_interpreter_t* s, _array_t* arr); -static int _get_array_pos(mb_interpreter_t* s, _array_t* arr, int* d, int c); -static int _get_array_index(mb_interpreter_t* s, _ls_node_t** l, _object_t* c, unsigned* index, bool_t* literally); -static bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type); -static int _set_array_elem(mb_interpreter_t* s, _ls_node_t* ast, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type); -static void _clear_array(_array_t* arr); -static bool_t _is_array(void* obj); -#ifdef MB_ENABLE_ARRAY_REF -static void _unref_array(_ref_t* ref, void* data); -#endif /* MB_ENABLE_ARRAY_REF */ - -#ifdef MB_ENABLE_COLLECTION_LIB -static _list_t* _create_list(mb_interpreter_t* s); -static void _destroy_list(_list_t* c); -static _dict_t* _create_dict(mb_interpreter_t* s); -static void _destroy_dict(_dict_t* c); -static _list_it_t* _create_list_it(_list_t* coll, bool_t lock); -static bool_t _destroy_list_it(_list_it_t* it); -static _list_it_t* _move_list_it_next(_list_it_t* it); -static _dict_it_t* _create_dict_it(_dict_t* coll, bool_t lock); -static bool_t _destroy_dict_it(_dict_it_t* it); -static _dict_it_t* _move_dict_it_next(_dict_it_t* it); -static void _unref_list(_ref_t* ref, void* data); -static void _unref_dict(_ref_t* ref, void* data); -static void _push_list(_list_t* coll, mb_value_t* val, _object_t* oarg); -static bool_t _pop_list(_list_t* coll, mb_value_t* val, mb_interpreter_t* s); -static bool_t _insert_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval); -static bool_t _set_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval); -static bool_t _remove_at_list(_list_t* coll, int_t idx); -static _ls_node_t* _node_at_list(_list_t* coll, int index); -static bool_t _at_list(_list_t* coll, int_t idx, mb_value_t* oval); -static bool_t _find_list(_list_t* coll, mb_value_t* val, int* idx); -static void _clear_list(_list_t* coll); -static void _sort_list(_list_t* coll); -static void _invalidate_list_cache(_list_t* coll); -static void _fill_ranged(_list_t* coll); -static void _set_dict(_dict_t* coll, mb_value_t* key, mb_value_t* val, _object_t* okey, _object_t* oval); -static bool_t _remove_dict(_dict_t* coll, mb_value_t* key); -static bool_t _find_dict(_dict_t* coll, mb_value_t* val, mb_value_t* oval); -static void _clear_dict(_dict_t* coll); -static bool_t _invalid_list_it(_list_it_t* it); -static bool_t _invalid_dict_it(_dict_it_t* it); -static bool_t _assign_with_it(_object_t* tgt, _object_t* src); -static int _clone_to_list(void* data, void* extra, _list_t* coll); -static int _clone_to_dict(void* data, void* extra, _dict_t* coll); -static int _copy_list_to_array(void* data, void* extra, _array_helper_t* h); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifdef MB_ENABLE_CLASS -typedef int (* _class_scope_walker)(void*, void*, void*); -typedef bool_t (* _class_meta_walker)(_class_t*, void*, void*); -static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n); -static void _begin_class(mb_interpreter_t* s); -static bool_t _end_class(mb_interpreter_t* s); -static void _unref_class(_ref_t* ref, void* data); -static void _destroy_class(_class_t* c); -static bool_t _traverse_class(_class_t* c, _class_scope_walker scope_walker, _class_meta_walker meta_walker, unsigned meta_depth, bool_t meta_walk_on_self, void* extra_data, void* ret); -static bool_t _link_meta_class(mb_interpreter_t* s, _class_t* derived, _class_t* base); -static void _unlink_meta_class(mb_interpreter_t* s, _class_t* derived); -static int _unlink_meta_instance(void* data, void* extra, _class_t* derived); -static int _clone_clsss_field(void* data, void* extra, void* n); -static bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret); -static int _search_class_meta_function(mb_interpreter_t* s, _class_t* instance, const char* n, _routine_t* _mb_unaligned * f); -static int _search_class_hash_and_compare_functions(mb_interpreter_t* s, _class_t* instance); -static bool_t _is_a_class(_class_t* instance, void* m, void* ret); -static bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret); -#ifdef MB_ENABLE_COLLECTION_LIB -static int _reflect_class_field(void* data, void* extra, void* d); -#endif /* MB_ENABLE_COLLECTION_LIB */ -static int _format_class_to_string(mb_interpreter_t* s, void** l, _class_t* instance, _object_t* out, bool_t* got_tostr); -static _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg); -static bool_t _is_valid_class_accessor_following_routine(mb_interpreter_t* s, _var_t* var, _ls_node_t* ast, _ls_node_t** out); -#endif /* MB_ENABLE_CLASS */ -static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f); -static int _begin_routine(mb_interpreter_t* s); -static bool_t _end_routine(mb_interpreter_t* s); -static void _begin_routine_parameter_list(mb_interpreter_t* s); -static void _end_routine_parameter_list(mb_interpreter_t* s); -static _object_t* _duplicate_parameter(void* data, void* extra, _running_context_t* running); -static _routine_t* _clone_routine(_routine_t* sub, void* c, bool_t toupval); -#ifdef MB_ENABLE_LAMBDA -static _running_context_t* _init_lambda(mb_interpreter_t* s, _routine_t* routine); -static void _unref_routine(_ref_t* ref, void* data); -static void _destroy_routine(mb_interpreter_t* s, _routine_t* r); -static void _mark_upvalue(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj, const char* n); -static void _try_mark_upvalue(mb_interpreter_t* s, _routine_t* r, _object_t* obj); -static _running_context_ref_t* _create_outer_scope(mb_interpreter_t* s); -static void _unref_outer_scope(_ref_t* ref, void* data); -static void _destroy_outer_scope(_running_context_ref_t* p); -static int _do_nothing_on_ht_for_lambda(void* data, void* extra); -static int _fill_with_upvalue(void* data, void* extra, _upvalue_scope_tuple_t* tuple); -static int _remove_filled_upvalue(void* data, void* extra, _ht_node_t* ht); -static int _fill_outer_scope(void* data, void* extra, _upvalue_scope_tuple_t* tuple); -static _running_context_t* _link_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak); -static _running_context_t* _unlink_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak); -static bool_t _is_valid_lambda_body_node(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj); -#endif /* MB_ENABLE_LAMBDA */ -#ifdef MB_ENABLE_CLASS -static _running_context_t* _reference_scope_by_class(mb_interpreter_t* s, _running_context_t* p, _class_t* c); -static _running_context_t* _push_scope_by_class(mb_interpreter_t* s, _running_context_t* p); -static _ls_node_t* _search_identifier_in_class(mb_interpreter_t* s, _class_t* instance, const char* n, _ht_node_t** ht, _running_context_t** sp); -#endif /* MB_ENABLE_CLASS */ -static _running_context_t* _reference_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r); -static _running_context_t* _push_weak_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r); -static _running_context_t* _push_scope_by_routine(mb_interpreter_t* s, _running_context_t* p); -static void _destroy_scope(mb_interpreter_t* s, _running_context_t* p); -static _running_context_t* _pop_weak_scope(mb_interpreter_t* s, _running_context_t* p); -static _running_context_t* _pop_scope(mb_interpreter_t* s, bool_t tidy); -static void _out_of_scope(mb_interpreter_t* s, _running_context_t* running, void* instance, _routine_t* routine, bool_t lose); -static _running_context_t* _find_scope(mb_interpreter_t* s, _running_context_t* p); -static _running_context_t* _get_root_scope(_running_context_t* scope); -#ifdef MB_ENABLE_LAMBDA -static _running_context_ref_t* _get_root_ref_scope(_running_context_ref_t* scope); -#endif /* MB_ENABLE_LAMBDA */ -static _running_context_t* _get_scope_to_add_routine(mb_interpreter_t* s); -static _ls_node_t* _search_identifier_in_scope_chain(mb_interpreter_t* s, _running_context_t* scope, const char* n, int pathing, _ht_node_t** ht, _running_context_t** sp); -static _array_t* _search_array_in_scope_chain(mb_interpreter_t* s, _array_t* i, _object_t** o); -static _var_t* _search_var_in_scope_chain(mb_interpreter_t* s, _var_t* i); -static _ls_node_t* _search_identifier_accessor(mb_interpreter_t* s, _running_context_t* scope, const char* n, _ht_node_t** ht, _running_context_t** sp, bool_t unknown_for_not_found); - -static _var_t* _create_var(_object_t** oobj, const char* n, size_t ns, bool_t dup_name); -static _object_t* _create_object(void); -static int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt, bool_t toupval, bool_t deep); -static int _dispose_object(_object_t* obj); -static int _destroy_object(void* data, void* extra); -static int _destroy_object_with_extra(void* data, void* extra); -static int _destroy_object_not_compile_time(void* data, void* extra); -static int _destroy_object_capsule_only(void* data, void* extra); -static int _do_nothing_on_object(void* data, void* extra); -static int _lose_object(void* data, void* extra); -static int _remove_source_object(void* data, void* extra); -static int _destroy_memory(void* data, void* extra); -static int _compare_numbers(const _object_t* first, const _object_t* second); -static bool_t _is_internal_object(_object_t* obj); -static _data_e _public_type_to_internal_type(mb_data_e t); -static mb_data_e _internal_type_to_public_type(_data_e t); -static int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn); -static int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl); -static int _create_internal_object_from_public_value(mb_value_t* pbl, _object_t** itn); -static int _compare_public_value_and_internal_object(mb_value_t* pbl, _object_t* itn); -static void _try_clear_intermediate_value(void* data, void* extra, mb_interpreter_t* s); -static void _remove_if_exist(void* data, void* extra, _ls_node_t* ls); -static void _destroy_var_arg(void* data, void* extra, _gc_t* gc); -static void _destroy_edge_objects(mb_interpreter_t* s); -static void _mark_edge_destroy_string(mb_interpreter_t* s, char* ch); -static void _destroy_lazy_objects(mb_interpreter_t* s); -static void _mark_lazy_destroy_string(mb_interpreter_t* s, char* ch); -static void _assign_public_value(mb_value_t* tgt, mb_value_t* src); -static void _swap_public_value(mb_value_t* tgt, mb_value_t* src); -static int _clear_scope_chain(mb_interpreter_t* s); -static int _dispose_scope_chain(mb_interpreter_t* s); -static void _tidy_scope_chain(mb_interpreter_t* s); -static void _tidy_intermediate_value(_ref_t* ref, void* data); -static _object_t* _eval_var_in_print(mb_interpreter_t* s, _object_t** val_ptr, _ls_node_t** ast, _object_t* obj); - -/** Interpretation */ - -static void _stepped(mb_interpreter_t* s, _ls_node_t* ast); -static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l, bool_t force_next); -static int _common_end_looping(mb_interpreter_t* s, _ls_node_t** l); -static int _common_keep_looping(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); -static int _execute_normal_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); -#ifdef MB_ENABLE_COLLECTION_LIB -static int _execute_ranged_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); -#endif /* MB_ENABLE_COLLECTION_LIB */ -static int _skip_to(mb_interpreter_t* s, _ls_node_t** l, mb_func_t f, _data_e t); -static int _skip_if_chunk(mb_interpreter_t* s, _ls_node_t** l); -static int _skip_struct(mb_interpreter_t* s, _ls_node_t** l, mb_func_t open_func, mb_func_t close_func); - -static _running_context_t* _create_running_context(bool_t create_var_dict); -static _parsing_context_t* _reset_parsing_context(_parsing_context_t* context); -static void _destroy_parsing_context(_parsing_context_t* _mb_unaligned * context); - -/** Interface processors */ - -#ifdef MB_ENABLE_MODULE -static _module_func_t* _create_module_func(mb_interpreter_t* s, mb_func_t f); -static int _ls_destroy_module_func(void* data, void* extra); -static int _ht_destroy_module_func_list(void* data, void* extra); -#endif /* MB_ENABLE_MODULE */ -static char* _generate_func_name(mb_interpreter_t* s, char* n, bool_t with_mod); -static int _register_func(mb_interpreter_t* s, char* n, mb_func_t f, bool_t local); -static int _remove_func(mb_interpreter_t* s, char* n, bool_t local); -static _ls_node_t* _find_func(mb_interpreter_t* s, char* n, bool_t* mod); - -static int _open_constant(mb_interpreter_t* s); -static int _close_constant(mb_interpreter_t* s); -static int _open_core_lib(mb_interpreter_t* s); -static int _close_core_lib(mb_interpreter_t* s); -static int _open_std_lib(mb_interpreter_t* s); -static int _close_std_lib(mb_interpreter_t* s); -#ifdef MB_ENABLE_COLLECTION_LIB -static int _open_coll_lib(mb_interpreter_t* s); -static int _close_coll_lib(mb_interpreter_t* s); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* ========================================================} */ - -/* -** {======================================================== -** Lib declarations -*/ - -/** Macro */ - -#ifdef MB_CP_VC -# if MB_CP_VC < 1300 -# define MB_FUNC 0 -# else /* MB_CP_VC < 1300 */ -# define MB_FUNC __FUNCTION__ -# endif /* MB_CP_VC < 1300 */ -#elif defined MB_CP_BORLANDC -# define MB_FUNC __FUNC__ -#elif defined MB_CP_PELLESC -# define MB_FUNC __func__ -#else /* MB_CP_VC */ -# define MB_FUNC __FUNCTION__ -#endif /* MB_CP_VC */ - -#ifdef MB_CP_VC -# if MB_CP_VC < 1300 -# define _do_nothing(__s, __l, __exit, __result) \ - do { \ - _ls_node_t* ast = 0; static int i = 0; ++i; \ - printf("Unaccessable function called %d times.\n", i); \ - ast = (_ls_node_t*)(*(__l)); \ - _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (__s)->source_file, DON(ast), MB_FUNC_ERR, __exit, (__result)); \ - } while(0) -# endif /* MB_CP_VC < 1300 */ -#endif /* MB_CP_VC */ -#ifndef _do_nothing -# define _do_nothing(__s, __l, __exit, __result) \ - do { \ - _ls_node_t* ast = (_ls_node_t*)(*(__l)); \ - _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (char*)MB_FUNC, DON(ast), MB_FUNC_ERR, __exit, (__result)); \ - } while(0); -#endif /* _do_nothing */ - -/** Core lib */ - -static int _core_dummy_assign(mb_interpreter_t* s, void** l); -static int _core_add(mb_interpreter_t* s, void** l); -static int _core_min(mb_interpreter_t* s, void** l); -static int _core_mul(mb_interpreter_t* s, void** l); -static int _core_div(mb_interpreter_t* s, void** l); -static int _core_mod(mb_interpreter_t* s, void** l); -static int _core_pow(mb_interpreter_t* s, void** l); -static int _core_open_bracket(mb_interpreter_t* s, void** l); -static int _core_close_bracket(mb_interpreter_t* s, void** l); -static int _core_neg(mb_interpreter_t* s, void** l); -static int _core_equal(mb_interpreter_t* s, void** l); -static int _core_less(mb_interpreter_t* s, void** l); -static int _core_greater(mb_interpreter_t* s, void** l); -static int _core_less_equal(mb_interpreter_t* s, void** l); -static int _core_greater_equal(mb_interpreter_t* s, void** l); -static int _core_not_equal(mb_interpreter_t* s, void** l); -static int _core_and(mb_interpreter_t* s, void** l); -static int _core_or(mb_interpreter_t* s, void** l); -static int _core_not(mb_interpreter_t* s, void** l); -static int _core_is(mb_interpreter_t* s, void** l); -static int _core_let(mb_interpreter_t* s, void** l); -static int _core_dim(mb_interpreter_t* s, void** l); -static int _core_if(mb_interpreter_t* s, void** l); -static int _core_then(mb_interpreter_t* s, void** l); -static int _core_elseif(mb_interpreter_t* s, void** l); -static int _core_else(mb_interpreter_t* s, void** l); -static int _core_endif(mb_interpreter_t* s, void** l); -static int _core_for(mb_interpreter_t* s, void** l); -#ifdef MB_ENABLE_COLLECTION_LIB -static int _core_in(mb_interpreter_t* s, void** l); -#endif /* MB_ENABLE_COLLECTION_LIB */ -static int _core_to(mb_interpreter_t* s, void** l); -static int _core_step(mb_interpreter_t* s, void** l); -static int _core_next(mb_interpreter_t* s, void** l); -static int _core_while(mb_interpreter_t* s, void** l); -static int _core_wend(mb_interpreter_t* s, void** l); -static int _core_do(mb_interpreter_t* s, void** l); -static int _core_until(mb_interpreter_t* s, void** l); -static int _core_exit(mb_interpreter_t* s, void** l); -static int _core_goto(mb_interpreter_t* s, void** l); -static int _core_gosub(mb_interpreter_t* s, void** l); -static int _core_return(mb_interpreter_t* s, void** l); -static int _core_call(mb_interpreter_t* s, void** l); -static int _core_def(mb_interpreter_t* s, void** l); -static int _core_enddef(mb_interpreter_t* s, void** l); -static int _core_args(mb_interpreter_t* s, void** l); -#ifdef MB_ENABLE_CLASS -static int _core_class(mb_interpreter_t* s, void** l); -static int _core_endclass(mb_interpreter_t* s, void** l); -static int _core_new(mb_interpreter_t* s, void** l); -static int _core_var(mb_interpreter_t* s, void** l); -static int _core_reflect(mb_interpreter_t* s, void** l); -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_LAMBDA -static int _core_lambda(mb_interpreter_t* s, void** l); -#endif /* MB_ENABLE_LAMBDA */ -#ifdef MB_ENABLE_ALLOC_STAT -static int _core_mem(mb_interpreter_t* s, void** l); -#endif /* MB_ENABLE_ALLOC_STAT */ -static int _core_type(mb_interpreter_t* s, void** l); -static int _core_import(mb_interpreter_t* s, void** l); -static int _core_end(mb_interpreter_t* s, void** l); - -/** Standard lib */ - -static int _std_abs(mb_interpreter_t* s, void** l); -static int _std_sgn(mb_interpreter_t* s, void** l); -static int _std_sqr(mb_interpreter_t* s, void** l); -static int _std_floor(mb_interpreter_t* s, void** l); -static int _std_ceil(mb_interpreter_t* s, void** l); -static int _std_fix(mb_interpreter_t* s, void** l); -static int _std_round(mb_interpreter_t* s, void** l); -static int _std_srnd(mb_interpreter_t* s, void** l); -static int _std_rnd(mb_interpreter_t* s, void** l); -static int _std_sin(mb_interpreter_t* s, void** l); -static int _std_cos(mb_interpreter_t* s, void** l); -static int _std_tan(mb_interpreter_t* s, void** l); -static int _std_asin(mb_interpreter_t* s, void** l); -static int _std_acos(mb_interpreter_t* s, void** l); -static int _std_atan(mb_interpreter_t* s, void** l); -static int _std_exp(mb_interpreter_t* s, void** l); -static int _std_log(mb_interpreter_t* s, void** l); -static int _std_asc(mb_interpreter_t* s, void** l); -static int _std_chr(mb_interpreter_t* s, void** l); -static int _std_left(mb_interpreter_t* s, void** l); -static int _std_mid(mb_interpreter_t* s, void** l); -static int _std_right(mb_interpreter_t* s, void** l); -static int _std_str(mb_interpreter_t* s, void** l); -static int _std_val(mb_interpreter_t* s, void** l); -static int _std_len(mb_interpreter_t* s, void** l); -static int _std_get(mb_interpreter_t* s, void** l); -static int _std_set(mb_interpreter_t* s, void** l); -static int _std_print(mb_interpreter_t* s, void** l); -static int _std_input(mb_interpreter_t* s, void** l); - -/** Collection lib */ - -#ifdef MB_ENABLE_COLLECTION_LIB -static int _coll_list(mb_interpreter_t* s, void** l); -static int _coll_dict(mb_interpreter_t* s, void** l); -static int _coll_push(mb_interpreter_t* s, void** l); -static int _coll_pop(mb_interpreter_t* s, void** l); -static int _coll_peek(mb_interpreter_t* s, void** l); -static int _coll_insert(mb_interpreter_t* s, void** l); -static int _coll_sort(mb_interpreter_t* s, void** l); -static int _coll_exist(mb_interpreter_t* s, void** l); -static int _coll_index_of(mb_interpreter_t* s, void** l); -static int _coll_remove(mb_interpreter_t* s, void** l); -static int _coll_clear(mb_interpreter_t* s, void** l); -static int _coll_clone(mb_interpreter_t* s, void** l); -static int _coll_to_array(mb_interpreter_t* s, void** l); -static int _coll_iterator(mb_interpreter_t* s, void** l); -static int _coll_move_next(mb_interpreter_t* s, void** l); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/** Lib information */ - -#define _CORE_ID_TYPE "TYPE" - -static const _func_t _core_libs[] = { - { _DUMMY_ASSIGN_CHAR, _core_dummy_assign }, - { "+", _core_add }, - { "-", _core_min }, - { "*", _core_mul }, - { "/", _core_div }, - { "MOD", _core_mod }, - { "^", _core_pow }, - { "(", _core_open_bracket }, - { ")", _core_close_bracket }, - { 0, _core_neg }, - - { "=", _core_equal }, - { "<", _core_less }, - { ">", _core_greater }, - { "<=", _core_less_equal }, - { ">=", _core_greater_equal }, - { "<>", _core_not_equal }, - - { "AND", _core_and }, - { "OR", _core_or }, - { "NOT", _core_not }, - - { "IS", _core_is }, - - { "LET", _core_let }, - { "DIM", _core_dim }, - - { "IF", _core_if }, - { "THEN", _core_then }, - { "ELSEIF", _core_elseif }, - { "ELSE", _core_else }, - { "ENDIF", _core_endif }, - - { "FOR", _core_for }, -#ifdef MB_ENABLE_COLLECTION_LIB - { "IN", _core_in }, -#endif /* MB_ENABLE_COLLECTION_LIB */ - { "TO", _core_to }, - { "STEP", _core_step }, - { "NEXT", _core_next }, - { "WHILE", _core_while }, - { "WEND", _core_wend }, - { "DO", _core_do }, - { "UNTIL", _core_until }, - - { "EXIT", _core_exit }, - { "GOTO", _core_goto }, - { "GOSUB", _core_gosub }, - { "RETURN", _core_return }, - - { "CALL", _core_call }, - { "DEF", _core_def }, - { "ENDDEF", _core_enddef }, - { _VAR_ARGS_STR, _core_args }, - -#ifdef MB_ENABLE_CLASS - { "CLASS", _core_class }, - { "ENDCLASS", _core_endclass }, - { "NEW", _core_new }, - { "VAR", _core_var }, - { "REFLECT", _core_reflect }, -#endif /* MB_ENABLE_CLASS */ - -#ifdef MB_ENABLE_LAMBDA - { "LAMBDA", _core_lambda }, -#endif /* MB_ENABLE_LAMBDA */ - -#ifdef MB_ENABLE_ALLOC_STAT - { "MEM", _core_mem }, -#endif /* MB_ENABLE_ALLOC_STAT */ - - { _CORE_ID_TYPE, _core_type }, - { "IMPORT", _core_import }, - { "END", _core_end } -}; - -#define _STD_ID_VAL "VAL" -#define _STD_ID_LEN "LEN" -#define _STD_ID_GET "GET" -#define _STD_ID_SET "SET" - -static const _func_t _std_libs[] = { - { "ABS", _std_abs }, - { "SGN", _std_sgn }, - { "SQR", _std_sqr }, - { "FLOOR", _std_floor }, - { "CEIL", _std_ceil }, - { "FIX", _std_fix }, - { "ROUND", _std_round }, - { "SRND", _std_srnd }, - { "RND", _std_rnd }, - { "SIN", _std_sin }, - { "COS", _std_cos }, - { "TAN", _std_tan }, - { "ASIN", _std_asin }, - { "ACOS", _std_acos }, - { "ATAN", _std_atan }, - { "EXP", _std_exp }, - { "LOG", _std_log }, - - { "ASC", _std_asc }, - { "CHR", _std_chr }, - { "LEFT", _std_left }, - { "MID", _std_mid }, - { "RIGHT", _std_right }, - { "STR", _std_str }, - { _STD_ID_VAL, _std_val }, - - { _STD_ID_LEN, _std_len }, - { _STD_ID_GET, _std_get }, - { _STD_ID_SET, _std_set }, - - { "PRINT", _std_print }, - { "INPUT", _std_input } -}; - -#ifdef MB_ENABLE_COLLECTION_LIB -# define _COLL_ID_LIST "LIST" -# define _COLL_ID_DICT "DICT" -# define _COLL_ID_PUSH "PUSH" -# define _COLL_ID_POP "POP" -# define _COLL_ID_PEEK "PEEK" -# define _COLL_ID_INSERT "INSERT" -# define _COLL_ID_SORT "SORT" -# define _COLL_ID_EXIST "EXIST" -# define _COLL_ID_INDEX_OF "INDEX_OF" -# define _COLL_ID_REMOVE "REMOVE" -# define _COLL_ID_CLEAR "CLEAR" -# define _COLL_ID_CLONE "CLONE" -# define _COLL_ID_TO_ARRAY "TO_ARRAY" -# define _COLL_ID_ITERATOR "ITERATOR" -# define _COLL_ID_MOVE_NEXT "MOVE_NEXT" - -static const _func_t _coll_libs[] = { - { _COLL_ID_LIST, _coll_list }, - { _COLL_ID_DICT, _coll_dict }, - { _COLL_ID_PUSH, _coll_push }, - { _COLL_ID_POP, _coll_pop }, - { _COLL_ID_PEEK, _coll_peek }, - { _COLL_ID_INSERT, _coll_insert }, - { _COLL_ID_SORT, _coll_sort }, - { _COLL_ID_EXIST, _coll_exist }, - { _COLL_ID_INDEX_OF, _coll_index_of }, - { _COLL_ID_REMOVE, _coll_remove }, - { _COLL_ID_CLEAR, _coll_clear }, - { _COLL_ID_CLONE, _coll_clone }, - { _COLL_ID_TO_ARRAY, _coll_to_array }, - { _COLL_ID_ITERATOR, _coll_iterator }, - { _COLL_ID_MOVE_NEXT, _coll_move_next } -}; -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* ========================================================} */ - -/* -** {======================================================== -** Private function definitions -*/ - -/** List operations */ - -static int _ls_cmp_data(void* node, void* info) { - _ls_node_t* n = (_ls_node_t*)node; - - return (n->data == info) ? 0 : 1; -} - -static int _ls_cmp_extra(void* node, void* info) { - _ls_node_t* n = (_ls_node_t*)node; - - return (n->extra == info) ? 0 : 1; -} - -static int _ls_cmp_extra_object(void* node, void* info) { - _ls_node_t* n = (_ls_node_t*)node; - - return _ht_cmp_object(n->extra, info); -} - -static int _ls_cmp_extra_string(void* node, void* info) { - _ls_node_t* n = (_ls_node_t*)node; - char* s1 = (char*)n->extra; - char* s2 = (char*)info; - - return strcmp(s1, s2); -} - -#ifdef MB_ENABLE_MODULE -static int _ls_cmp_module_func(void* node, void* info) { - _module_func_t* m = (_module_func_t*)node; - mb_interpreter_t* s = (mb_interpreter_t*)info; - - return strcmp(m->module, s->with_module); -} -#endif /* MB_ENABLE_MODULE */ - -static _ls_node_t* _ls_create_node(void* data) { - _ls_node_t* result = 0; - - result = (_ls_node_t*)mb_malloc(sizeof(_ls_node_t)); - mb_assert(result); - memset(result, 0, sizeof(_ls_node_t)); - result->data = data; - - return result; -} - -static _ls_node_t* _ls_create(void) { - _ls_node_t* result = 0; - - result = _ls_create_node(0); - - return result; -} - -static _ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp, int* idx) { - _ls_node_t* result = 0; - - mb_assert(list && data && cmp); - - if(idx) *idx = 0; - - list = list->next; - while(list) { - if(!cmp(list->data, data)) { - result = list; - - break; - } - list = list->next; - if(idx) ++*idx; - } - - return result; -} - -static _ls_node_t* _ls_back(_ls_node_t* node) { - _ls_node_t* result = node; - - result = result->prev; - - return result; -} - -static _ls_node_t* _ls_pushback(_ls_node_t* list, void* data) { - _ls_node_t* result = 0; - _ls_node_t* tmp = 0; - - mb_assert(list); - - result = _ls_create_node(data); - - tmp = _ls_back(list); - if(!tmp) - tmp = list; - tmp->next = result; - result->prev = tmp; - list->prev = result; - list->data = (char*)list->data + 1; - - return result; -} - -static void* _ls_popback(_ls_node_t* list) { - void* result = 0; - _ls_node_t* tmp = 0; - - mb_assert(list); - - tmp = _ls_back(list); - if(tmp) { - result = tmp->data; - if(list != tmp->prev) - list->prev = tmp->prev; - else - list->prev = 0; - tmp->prev->next = 0; - safe_free(tmp); - list->data = (char*)list->data - 1; - } - - return result; -} - -static _ls_node_t* _ls_front(_ls_node_t* node) { - _ls_node_t* result = node; - - result = result->next; - - return result; -} - -static void* _ls_popfront(_ls_node_t* list) { - void* result = 0; - _ls_node_t* tmp = 0; - - mb_assert(list); - - tmp = _ls_front(list); - if(tmp) { - result = tmp->data; - list->next = tmp->next; - if(tmp->next) - tmp->next->prev = list; - if(!list->next) - list->prev = 0; - tmp->prev = tmp->next = 0; - safe_free(tmp); - list->data = (char*)list->data - 1; - } - - return result; -} - -static _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data) { - _ls_node_t* result = 0; - _ls_node_t* tmp = 0; - - mb_assert(list); - - tmp = list->next; - while(tmp && index) { - tmp = tmp->next; - --index; - } - if(!tmp) { - if(index == 0) - result = _ls_pushback(list, data); - } else { - result = _ls_create_node(data); - tmp->prev->next = result; - result->prev = tmp->prev; - result->next = tmp; - tmp->prev = result; - } - list->data = (char*)list->data + 1; - - return result; -} - -static unsigned _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op) { - unsigned result = 0; - - mb_assert(list && node); - - if(node->prev) - node->prev->next = node->next; - if(node->next) - node->next->prev = node->prev; - if(list->prev == node) - list->prev = node->prev; - if(list->prev == list) - list->prev = 0; - if(op) - op(node->data, node->extra); - safe_free(node); - ++result; - list->data = (char*)list->data - 1; - - return result; -} - -static unsigned _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op) { - unsigned result = 0; - _ls_node_t* tmp = 0; - - mb_assert(list && cmp); - - tmp = list->next; - while(tmp) { - if(cmp(tmp, info) == 0) { - if(tmp->prev) - tmp->prev->next = tmp->next; - if(tmp->next) - tmp->next->prev = tmp->prev; - if(list->prev == tmp) - list->prev = tmp->prev; - if(list->prev == list) - list->prev = 0; - if(op) - op(tmp->data, tmp->extra); - safe_free(tmp); - ++result; - list->data = (char*)list->data - 1; - - break; - } - tmp = tmp->next; - } - - return result; -} - -static unsigned _ls_foreach(_ls_node_t* list, _ls_operation op) { - unsigned idx = 0; - int opresult = _OP_RESULT_NORMAL; - _ls_node_t* node = 0; - _ls_node_t* tmp = 0; - - mb_assert(list && op); - - node = list->next; - while(node) { - opresult = op(node->data, node->extra); - ++idx; - tmp = node; - node = node->next; - - if(_OP_RESULT_DEL_NODE == opresult) { - tmp->prev->next = node; - if(node) - node->prev = tmp->prev; - safe_free(tmp); - list->data = (char*)list->data - 1; - } - } - - return idx; -} - -static _ls_node_t* _ls_sort(_ls_node_t* _mb_unaligned * list, _ls_compare cmp) { - /* Copyright 2001 Simon Tatham, http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c */ - bool_t is_circular = false, is_double = true; - _ls_node_t* p, * q, * e, * tail, * oldhead; - int insize, nmerges, psize, qsize, i; - _ls_node_t* lst = 0; - - mb_assert(list && *list && cmp); - - lst = *list; - if(lst) lst = lst->next; - - if(!lst) - return 0; - - insize = 1; - - while(1) { - p = lst; - oldhead = lst; - lst = 0; - tail = 0; - - nmerges = 0; - - while(p) { - nmerges++; - q = p; - psize = 0; - for(i = 0; i < insize; i++) { - psize++; - if(is_circular) - q = (q->next == oldhead ? 0 : q->next); - else - q = q->next; - if(!q) - break; - } - - qsize = insize; - - while(psize > 0 || (qsize > 0 && q)) { - if(psize == 0) { - e = q; q = q->next; qsize--; - if(is_circular && q == oldhead) q = 0; - } else if(qsize == 0 || !q) { - e = p; p = p->next; psize--; - if(is_circular && p == oldhead) p = 0; - } else if(cmp(p->data, q->data) <= 0) { - e = p; p = p->next; psize--; - if(is_circular && p == oldhead) p = 0; - } else { - e = q; q = q->next; qsize--; - if(is_circular && q == oldhead) q = 0; - } - - if(tail) - tail->next = e; - else - lst = e; - if(is_double) - e->prev = tail; - tail = e; - } - - p = q; - } - if(is_circular) { - tail->next = lst; - if(is_double) - lst->prev = tail; - } else { - tail->next = 0; - } - - if(nmerges <= 1) { - (*list)->next = lst; - (*list)->prev = tail; - - lst->prev = *list; - - return *list; - } - - insize *= 2; - } -} - -static unsigned _ls_count(_ls_node_t* list) { - union { void* p; unsigned u; } tmp; - - mb_assert(list); - - tmp.p = list->data; - - return tmp.u; -} - -static bool_t _ls_empty(_ls_node_t* list) { - bool_t result = false; - - mb_assert(list); - - result = 0 == list->next; - - return result; -} - -static void _ls_clear(_ls_node_t* list) { - _ls_node_t* tmp = 0; - - mb_assert(list); - - list->data = 0; - - tmp = list; - list = list->next; - tmp->next = 0; - tmp->prev = 0; - - while(list) { - tmp = list; - list = list->next; - safe_free(tmp); - } -} - -static void _ls_destroy(_ls_node_t* list) { - _ls_clear(list); - safe_free(list); -} - -static int _ls_free_extra(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - mb_unrefvar(data); - - mb_assert(extra); - - safe_free(extra); - - return result; -} - -/** Dictionary operations */ - -static unsigned _ht_hash_object(void* ht, void* d) { - unsigned result = 0; - _ht_node_t* self = (_ht_node_t*)ht; - _object_t* o = (_object_t*)d; - size_t i = 0; - unsigned h = 0; -#ifdef MB_ENABLE_CLASS - _object_t val; -#endif /* MB_ENABLE_CLASS */ - - mb_assert(ht); - - h = o->type; - switch(o->type) { - case _DT_STRING: - h = 5 * h + _ht_hash_string(ht, o->data.string); - if(self->array_size == 1) - result = 0; - else - result = h % self->array_size; - - break; -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - if(o->data.instance->hash) { - mb_interpreter_t* s = o->data.instance->ref.s; - _ls_node_t ast; - _ls_node_t* tmp = * - mb_value_t va[1]; - mb_make_nil(va[0]); - memset(&ast, 0, sizeof(_ls_node_t)); - if(_eval_routine(s, &tmp, va, 1, o->data.instance->hash, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { - _MAKE_NIL(&val); - _public_value_to_internal_object(&s->running_context->intermediate_value, &val); - if(val.type != _DT_INT) { - int ignored = MB_FUNC_OK; - _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, o, MB_FUNC_ERR, _exit, ignored); - } - - o = &val; - } - } - - goto _default; -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - if(o->data.usertype_ref->hash) { - if(self->array_size == 1) { - result = 0; - } else { - h = 5 * h + o->data.usertype_ref->hash(o->data.usertype_ref->ref.s, o->data.usertype_ref->usertype); - result = h % self->array_size; - } - - break; - } - - goto _default; -#endif /* MB_ENABLE_USERTYPE_REF */ - default: -#if defined MB_ENABLE_CLASS || defined MB_ENABLE_USERTYPE_REF -_default: -#endif /* MB_ENABLE_CLASS || MB_ENABLE_USERTYPE_REF */ - if(self->array_size == 1) { - result = 0; - } else { - for(i = 0; i < sizeof(_raw_t); ++i) - h = 5 * h + o->data.raw[i]; - result = h % self->array_size; - } - - break; - } - - goto _exit; /* Avoid an unreferenced label warning */ - -_exit: - return result; -} - -static unsigned _ht_hash_string(void* ht, void* d) { - unsigned result = 0; - _ht_node_t* self = (_ht_node_t*)ht; - char* s = (char*)d; - unsigned h = 0; - - mb_assert(ht); - - if(self->array_size == 1) { - result = 0; - } else { - for( ; *s; ++s) - h = 5 * h + *s; - result = h % self->array_size; - } - - return result; -} - -static unsigned _ht_hash_intptr(void* ht, void* d) { - unsigned result = 0; - _ht_node_t* self = (_ht_node_t*)ht; - intptr_t i = *(intptr_t*)d; - - mb_assert(ht); - - if(self->array_size == 1) - result = 0; - else - result = (unsigned)(i % self->array_size); - - return result; -} - -static unsigned _ht_hash_ref(void* ht, void* d) { - unsigned result = 0; - _ht_node_t* self = (_ht_node_t*)ht; - _ref_t* ref = (_ref_t*)d; - - mb_assert(ht); - - if(self->array_size == 1) { - result = 0; - } else { - result = (unsigned)(intptr_t)ref; - result %= self->array_size; - } - - return result; -} - -static int _ht_cmp_object(void* d1, void* d2) { - _object_t* o1 = (_object_t*)d1; - _object_t* o2 = (_object_t*)d2; - int i = 0; -#ifdef MB_ENABLE_CLASS - _routine_t* cmp = 0; - _object_t val; - bool_t fst = true; -#endif /* MB_ENABLE_CLASS */ - - if(o1->type < o2->type) - return -1; - else if(o1->type > o2->type) - return 1; - - switch(o1->type) { - case _DT_STRING: - return _ht_cmp_string(o1->data.string, o2->data.string); -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - if(o1->data.instance->compare) { - cmp = o1->data.instance->compare; - fst = true; - } else if(o2->data.instance->compare) { - cmp = o2->data.instance->compare; - fst = false; - } - if(cmp) { - mb_interpreter_t* s = o1->data.instance->ref.s; - _ls_node_t ast; - _ls_node_t* tmp = * - mb_value_t va[1]; - mb_make_nil(va[0]); - _internal_object_to_public_value(fst ? o2 : o1, &va[0]); - memset(&ast, 0, sizeof(_ls_node_t)); - if(_eval_routine(s, &tmp, va, 1, cmp, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { - _MAKE_NIL(&val); - _public_value_to_internal_object(&s->running_context->intermediate_value, &val); - if(val.type != _DT_INT) { - int ignored = MB_FUNC_OK; - _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, o1, MB_FUNC_ERR, _exit, ignored); - } - - return (int)(fst ? val.data.integer : -val.data.integer); - } - } - - goto _default; -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - if(o1->data.usertype_ref->cmp) - return o1->data.usertype_ref->cmp(o1->data.usertype_ref->ref.s, o1->data.usertype_ref->usertype, o2->data.usertype_ref->usertype); - else if(o2->data.usertype_ref->cmp) - return o2->data.usertype_ref->cmp(o1->data.usertype_ref->ref.s, o1->data.usertype_ref->usertype, o2->data.usertype_ref->usertype); - - goto _default; -#endif /* MB_ENABLE_USERTYPE_REF */ - default: -#if defined MB_ENABLE_CLASS || defined MB_ENABLE_USERTYPE_REF -_default: -#endif /* MB_ENABLE_CLASS || MB_ENABLE_USERTYPE_REF */ - if(mb_is_little_endian()) { - for(i = (int)sizeof(_raw_t) - 1; i >= 0; --i) { - if(o1->data.raw[i] < o2->data.raw[i]) - return -1; - else if(o1->data.raw[i] > o2->data.raw[i]) - return 1; - } - } else { - for(i = 0; i < (int)sizeof(_raw_t); ++i) { - if(o1->data.raw[i] < o2->data.raw[i]) - return -1; - else if(o1->data.raw[i] > o2->data.raw[i]) - return 1; - } - } - - break; - } - - goto _exit; /* Avoid an unreferenced label warning */ - -_exit: - return 0; -} - -static int _ht_cmp_string(void* d1, void* d2) { - char* s1 = (char*)d1; - char* s2 = (char*)d2; - - return strcmp(s1, s2); -} - -static int _ht_cmp_intptr(void* d1, void* d2) { - intptr_t i1 = *(intptr_t*)d1; - intptr_t i2 = *(intptr_t*)d2; - - if(i1 < i2) - return -1; - else if(i1 > i2) - return 1; - - return 0; -} - -static int _ht_cmp_ref(void* d1, void* d2) { - _ref_t* r1 = (_ref_t*)d1; - _ref_t* r2 = (_ref_t*)d2; - intptr_t i = (intptr_t)r1 - (intptr_t)r2; - - if(i < 0) - return -1; - else if(i > 0) - return 1; - - return 0; -} - -static _ht_node_t* _ht_create(unsigned size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra) { - const unsigned array_size = size ? size : _HT_ARRAY_SIZE_DEFAULT; - _ht_node_t* result = 0; - unsigned ul = 0; - - if(!cmp) - cmp = _ht_cmp_intptr; - if(!hs) - hs = _ht_hash_intptr; - - result = (_ht_node_t*)mb_malloc(sizeof(_ht_node_t)); - result->free_extra = freeextra; - result->compare = cmp; - result->hash = hs; - result->array_size = array_size; - result->count = 0; -#if _LAZY_HASH_TABLE - mb_unrefvar(ul); - - result->array = 0; -#else /* _LAZY_HASH_TABLE */ - result->array = (_ls_node_t**)mb_malloc(sizeof(_ls_node_t*) * result->array_size); - for(ul = 0; ul < result->array_size; ++ul) - result->array[ul] = _ls_create(); -#endif /* _LAZY_HASH_TABLE */ - - return result; -} - -static _ls_node_t* _ht_find(_ht_node_t* ht, void* key) { - _ls_node_t* result = 0; - _ls_node_t* bucket = 0; - unsigned hash_code = 0; - - mb_assert(ht && key); - - hash_code = ht->hash(ht, key); - if(ht->array && ht->array[hash_code]) { - bucket = ht->array[hash_code]; - bucket = bucket->next; - } - while(bucket) { - if(ht->compare(bucket->extra, key) == 0) { - result = bucket; - - break; - } - bucket = bucket->next; - } - - return result; -} - -static unsigned _ht_set_or_insert(_ht_node_t* ht, void* key, void* value) { - unsigned result = 0; - _ls_node_t* bucket = 0; - unsigned hash_code = 0; - unsigned ul = 0; - - mb_assert(ht && key); - - bucket = _ht_find(ht, key); - if(bucket) { /* Update */ - bucket->data = value; - ++result; - } else { /* Insert */ - hash_code = ht->hash(ht, key); - if(!ht->array) { - ht->array = (_ls_node_t**)mb_malloc(sizeof(_ls_node_t*) * ht->array_size); - for(ul = 0; ul < ht->array_size; ++ul) - ht->array[ul] = 0; - } - if(!ht->array[hash_code]) - ht->array[hash_code] = _ls_create(); - bucket = ht->array[hash_code]; - bucket = _ls_pushback(bucket, value); - mb_assert(bucket); - bucket->extra = key; - ++ht->count; - ++result; - } - - return result; -} - -static unsigned _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp) { - unsigned result = 0; - unsigned hash_code = 0; - _ls_node_t* bucket = 0; - - mb_assert(ht && key); - - if(!cmp) - cmp = _ls_cmp_extra; - - bucket = _ht_find(ht, key); - hash_code = ht->hash(ht, key); - if(ht->array && ht->array[hash_code]) { - bucket = ht->array[hash_code]; - result = _ls_try_remove(bucket, key, cmp, ht->free_extra); - ht->count -= result; - } - - return result; -} - -static unsigned _ht_foreach(_ht_node_t* ht, _ht_operation op) { - unsigned result = 0; - _ls_node_t* bucket = 0; - unsigned ul = 0; - - if(ht->array) { - for(ul = 0; ul < ht->array_size; ++ul) { - bucket = ht->array[ul]; - if(bucket) - result += _ls_foreach(bucket, op); - } - } - - return result; -} - -static unsigned _ht_count(_ht_node_t* ht) { - mb_assert(ht); - - return ht->count; -} - -static void _ht_clear(_ht_node_t* ht) { - unsigned ul = 0; - - mb_assert(ht); - - if(ht->array) { - for(ul = 0; ul < ht->array_size; ++ul) { - if(ht->array[ul]) - _ls_clear(ht->array[ul]); - } - ht->count = 0; - } -} - -static void _ht_destroy(_ht_node_t* ht) { - unsigned ul = 0; - - mb_assert(ht); - - if(ht->array) { - if(ht->free_extra) - _ht_foreach(ht, ht->free_extra); - - for(ul = 0; ul < ht->array_size; ++ul) { - if(ht->array[ul]) - _ls_destroy(ht->array[ul]); - } - - safe_free(ht->array); - } - safe_free(ht); -} - -static int _ht_remove_exist(void* data, void* extra, _ht_node_t* ht) { - int result = _OP_RESULT_NORMAL; - mb_unrefvar(data); - - if(_ht_find(ht, extra)) - _ht_remove(ht, extra, 0); - - return result; -} - -/** Memory manipulations */ - -/* Initialize a chunk of resizable dynamic buffer */ -static void _init_dynamic_buffer(_dynamic_buffer_t* buf) { - mb_assert(buf); - - memset(buf->bytes, 0, sizeof(buf->bytes)); - buf->pointer.charp = buf->bytes; - buf->size = sizeof(buf->bytes); -} - -/* Dispose a chunk of resizable dynamic buffer */ -static void _dispose_dynamic_buffer(_dynamic_buffer_t* buf) { - mb_assert(buf); - - if(buf->pointer.charp != buf->bytes) { - safe_free(buf->pointer.charp); - } - buf->pointer.charp = 0; - buf->size = 0; -} - -/* Get the element count of a chunk of resizable dynamic buffer */ -static size_t _countof_dynamic_buffer(_dynamic_buffer_t* buf, size_t es) { - mb_assert(buf); - - return buf->size / es; -} - -/* Resize a chunk of resizable dynamic buffer */ -static void _resize_dynamic_buffer(_dynamic_buffer_t* buf, size_t es, size_t c) { - size_t as = es * c; - - mb_assert(buf); - - if(as > buf->size) { - if(buf->pointer.charp != buf->bytes) { - safe_free(buf->pointer.charp); - } - buf->pointer.charp = (char*)mb_malloc(as); - buf->size = as; - } -} - -/* Allocate a chunk of memory with a specific size */ -static void* mb_malloc(size_t s) { - char* ret = 0; - size_t rs = s; - -#ifdef MB_ENABLE_ALLOC_STAT - if(!_MB_CHECK_MEM_TAG_SIZE(size_t, s)) - return 0; - rs += _MB_MEM_TAG_SIZE; -#endif /* MB_ENABLE_ALLOC_STAT */ - if(_mb_allocate_func) - ret = _mb_allocate_func((unsigned)rs); - else - ret = (char*)malloc(rs); - mb_assert(ret); -#ifdef MB_ENABLE_ALLOC_STAT - _mb_allocated += s; - ret += _MB_MEM_TAG_SIZE; - _MB_WRITE_MEM_TAG_SIZE(ret, s); -#endif /* MB_ENABLE_ALLOC_STAT */ - - return (void*)ret; -} - -/* Free a chunk of memory */ -static void mb_free(void* p) { - mb_assert(p); - -#ifdef MB_ENABLE_ALLOC_STAT - do { - size_t os = _MB_READ_MEM_TAG_SIZE(p); - _mb_allocated -= os; - p = (char*)p - _MB_MEM_TAG_SIZE; - } while(0); -#endif /* MB_ENABLE_ALLOC_STAT */ - if(_mb_free_func) - _mb_free_func((char*)p); - else - free(p); -} - -/* Compare two chunks of memory */ -static int mb_memcmp(void* l, void* r, size_t s) { - unsigned char* lc = (unsigned char*)l; - unsigned char* rc = (unsigned char*)r; - int i = 0; - - if(mb_is_little_endian()) { - for(i = (int)s - 1; i >= 0; i--) { - if(lc[i] < rc[i]) - return -1; - else if(lc[i] > rc[i]) - return 1; - } - } else { - for(i = 0; i < (int)s; i++) { - if(lc[i] < rc[i]) - return -1; - else if(lc[i] > rc[i]) - return 1; - } - } - - return 0; -} - -/* Detect whether a chunk of memory contains any non-zero byte */ -static 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; -} - -/* Duplicate a string */ -static char* mb_strdup(const char* p, size_t s) { -#ifdef MB_ENABLE_ALLOC_STAT - if(!s) { - s = _MB_READ_MEM_TAG_SIZE(p); - } - - return mb_memdup(p, (unsigned)s); -#else /* MB_ENABLE_ALLOC_STAT */ - if(s) - return mb_memdup(p, (unsigned)s); - - return mb_memdup(p, (unsigned)(strlen(p) + 1)); -#endif /* MB_ENABLE_ALLOC_STAT */ -} - -/* Change a string to upper case */ -static char* mb_strupr(char* s) { - char* t = s; - - while(*s) { - *s = toupper(*s); - ++s; - } - - return t; -} - -/* Determine whether it's running on a little endian platform */ -static bool_t mb_is_little_endian(void) { - int i = 1; - - return ((char*)&i)[0] == 1; -} - -/** Unicode handling */ - -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE -/* Map a UTF8 character string to a UTF16 (wide character) string */ -static int mb_bytes_to_wchar(const char* sz, wchar_t** out, size_t size) { - int result = MultiByteToWideChar(CP_UTF8, 0, sz, -1, 0, 0); - if((int)size >= result) - MultiByteToWideChar(CP_UTF8, 0, sz, -1, *out, result); - - return result; -} - -/* Map an ANSI character string to a UTF16 (wide character) string */ -static int mb_bytes_to_wchar_ansi(const char* sz, wchar_t** out, size_t size) { - int result = MultiByteToWideChar(CP_ACP, 0, sz, -1, 0, 0); - if((int)size >= result) - MultiByteToWideChar(CP_ACP, 0, sz, -1, *out, result); - - return result; -} - -/* Map a UTF16 (wide character) string to a UTF8 character string */ -static int mb_wchar_to_bytes(const wchar_t* sz, char** out, size_t size) { - int result = WideCharToMultiByte(CP_UTF8, 0, sz, -1, 0, 0, 0, 0); - if((int)size >= result) - WideCharToMultiByte(CP_UTF8, 0, sz, -1, *out, result, 0, 0); - - return result; -} -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ - -/* Determine whether a string begins with a BOM, and ignore it */ -static int mb_uu_getbom(const char** ch) { - if(!ch && !(*ch)) - return 0; - - if((*ch)[0] == -17 && (*ch)[1] == -69 && (*ch)[2] == -65) { - *ch += 3; - - return 3; - } else if((*ch)[0] == -2 && (*ch)[1] == -1) { - *ch += 2; - - return 2; - } - - return 0; -} - -#ifdef MB_ENABLE_UNICODE -/* Determine whether a buffer is a UTF8 encoded character, and return taken bytes */ -static int mb_uu_ischar(const char* ch) { - /* Copyright 2008, 2009 Bjoern Hoehrmann, http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ */ -# define _TAKE(__ch, __c, __r) do { __c = *__ch++; __r++; } while(0) -# define _COPY(__ch, __c, __r, __cp) do { _TAKE(__ch, __c, __r); __cp = (__cp << 6) | ((unsigned char)__c & 0x3Fu); } while(0) -# define _TRANS(__m, __cp, __g) do { __cp &= ((__g[(unsigned char)c] & __m) != 0); } while(0) -# define _TAIL(__ch, __c, __r, __cp, __g) do { _COPY(__ch, __c, __r, __cp); _TRANS(0x70, __cp, __g); } while(0) - - static const unsigned char range[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 - }; - - int result = 0; - unsigned codepoint = 0; - unsigned char type = 0; - char c = 0; - - if(!ch) - return 0; - - _TAKE(ch, c, result); - if(!(c & 0x80)) { - codepoint = (unsigned char)c; - - return 1; - } - - type = range[(unsigned char)c]; - codepoint = (0xFF >> type) & (unsigned char)c; - - switch(type) { - case 2: _TAIL(ch, c, result, codepoint, range); return result; - case 3: _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - case 4: _COPY(ch, c, result, codepoint); _TRANS(0x50, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - case 5: _COPY(ch, c, result, codepoint); _TRANS(0x10, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - case 6: _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - case 10: _COPY(ch, c, result, codepoint); _TRANS(0x20, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - case 11: _COPY(ch, c, result, codepoint); _TRANS(0x60, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; - default: return 0; - } - -# undef _TAKE -# undef _COPY -# undef _TRANS -# undef _TAIL -} - -/* Tell how many UTF8 character are there in a string */ -static int mb_uu_strlen(const char* ch) { - int result = 0; - - if(!ch) - return 0; - - while(*ch) { - int t = mb_uu_ischar(ch); - if(t <= 0) - return t; - ch += t; - result++; - } - - return result; -} - -/* Retrieve a sub string of a UTF8 string */ -static int mb_uu_substr(const char* ch, int begin, int count, char** o) { - int cnt = 0; - const char* b = 0; - const char* e = 0; - int l = 0; - - if(!ch || begin < 0 || count <= 0 || !o) - return -1; - - while(*ch) { - int t = mb_uu_ischar(ch); - if(t <= 0) - return t; - if(cnt == begin) { - b = ch; - - break; - } - ch += t; - cnt++; - } - - while(*ch) { - int t = mb_uu_ischar(ch); - if(t <= 0) - return t; - if(cnt == begin + count) { - e = ch; - - break; - } - ch += t; - e = ch; - cnt++; - } - - if(!(*ch) && (cnt != begin + count)) - return -1; - - l = (int)(e - b); - *o = (char*)mb_malloc(l + 1); - memcpy(*o, b, l); - (*o)[l] = _ZERO_CHAR; - - return l; -} -#endif /* MB_ENABLE_UNICODE */ - -/** Expression processing */ - -/* Determine whether a function is an operator */ -static bool_t _is_operator(mb_func_t op) { - return ( - (op == _core_dummy_assign) || - (op == _core_add) || - (op == _core_min) || - (op == _core_mul) || - (op == _core_div) || - (op == _core_mod) || - (op == _core_pow) || - (op == _core_open_bracket) || - (op == _core_close_bracket) || - (op == _core_equal) || - (op == _core_less) || - (op == _core_greater) || - (op == _core_less_equal) || - (op == _core_greater_equal) || - (op == _core_not_equal) || - (op == _core_and) || - (op == _core_or) || - (op == _core_is) - ); -} - -/* Determine whether a function is for flow control */ -static bool_t _is_flow(mb_func_t op) { - return ( - (op == _core_if) || - (op == _core_then) || - (op == _core_elseif) || - (op == _core_else) || - (op == _core_endif) || - (op == _core_for) || - (op == _core_to) || - (op == _core_step) || - (op == _core_next) || - (op == _core_while) || - (op == _core_wend) || - (op == _core_do) || - (op == _core_until) || - (op == _core_exit) || - (op == _core_goto) || - (op == _core_gosub) || - (op == _core_return) || - (op == _core_end) - ); -} - -/* Determine whether a function is unary */ -static bool_t _is_unary(mb_func_t op) { - return (op == _core_neg) || (op == _core_not); -} - -/* Determine whether a function is binary */ -static bool_t _is_binary(mb_func_t op) { - return ( - (op == _core_add) || - (op == _core_min) || - (op == _core_mul) || - (op == _core_div) || - (op == _core_mod) || - (op == _core_pow) || - (op == _core_equal) || - (op == _core_less) || - (op == _core_greater) || - (op == _core_less_equal) || - (op == _core_greater_equal) || - (op == _core_not_equal) || - (op == _core_and) || - (op == _core_or) || - (op == _core_is) - ); -} - -/* Get the priority of two operators */ -static char _get_priority(mb_func_t op1, mb_func_t op2) { - char result = _ZERO_CHAR; - int idx1 = 0; - int idx2 = 0; - - mb_assert(op1 && op2); - - idx1 = _get_priority_index(op1); - idx2 = _get_priority_index(op2); - mb_assert(idx1 < countof(_PRECEDE_TABLE) && idx2 < countof(_PRECEDE_TABLE[0])); - result = _PRECEDE_TABLE[idx1][idx2]; - - return result; -} - -/* Get the index of an operator in the priority table */ -static int _get_priority_index(mb_func_t op) { - int i = 0; - - mb_func_t funcs[] = { - _core_add, - _core_min, - _core_mul, - _core_div, - _core_mod, - _core_pow, - _core_open_bracket, - _core_close_bracket, - _core_dummy_assign, - _core_greater, - _core_less, - _core_greater_equal, - _core_less_equal, - _core_equal, - _core_not_equal, - _core_and, - _core_or, - _core_not, - _core_neg, - _core_is - }; - - mb_assert(op); - - for(i = 0; i < countof(funcs); i++) { - if(op == funcs[i]) - return i; - } - - mb_assert(0 && "Unknown operator."); - - return -1; -} - -/* Operate two operands */ -static _object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status) { - _object_t* result = 0; - _tuple3_t tp; - _tuple3_t* tpptr = 0; - int _status = 0; - - mb_assert(s && optr); - mb_assert(optr->type == _DT_FUNC); - - if(!opnd1) - return result; - - result = _create_object(); - - memset(&tp, 0, sizeof(_tuple3_t)); - tp.e1 = opnd1; - tp.e2 = opnd2; - tp.e3 = result; - tpptr = &tp; - - _status = (optr->data.func->pointer)(s, (void**)&tpptr); - if(status) - *status = _status; - if(_status != MB_FUNC_OK) { - if(_status != MB_FUNC_WARNING) { - safe_free(result); - } - _set_current_error(s, SE_RN_OPERATION_FAILED, 0); -#ifdef MB_ENABLE_SOURCE_TRACE - _set_error_pos(s, optr->source_pos, optr->source_row, optr->source_col); -#else /* MB_ENABLE_SOURCE_TRACE */ - _set_error_pos(s, 0, 0, 0); -#endif /* MB_ENABLE_SOURCE_TRACE */ - } - - return result; -} - -/* Determine whether an object is an expression termination */ -static bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj) { - bool_t result = false; - - mb_assert(s && obj); - - result = ( - (obj->type == _DT_EOS) || - (obj->type == _DT_SEP) || - (obj->type == _DT_FUNC && - (obj->data.func->pointer == _core_then || - obj->data.func->pointer == _core_elseif || - obj->data.func->pointer == _core_else || - obj->data.func->pointer == _core_endif || - obj->data.func->pointer == _core_to || - obj->data.func->pointer == _core_step)) - ); - - return result; -} - -/* Determine whether an object is an unexpected calculation result */ -static bool_t _is_unexpected_calc_type(mb_interpreter_t* s, _object_t* obj) { - mb_assert(s); - - return !obj || ( - (obj->type == _DT_FUNC) || - (obj->type == _DT_LABEL) || -#ifdef MB_ENABLE_LAMBDA - (obj->type == _DT_OUTER_SCOPE) || -#endif /* MB_ENABLE_LAMBDA */ - (obj->type == _DT_SEP) || -#ifdef MB_ENABLE_SOURCE_TRACE - (obj->type == _DT_PREV_IMPORT) || - (obj->type == _DT_POST_IMPORT) || -#endif /* MB_ENABLE_SOURCE_TRACE */ - (obj->type == _DT_EOS) - ); -} - -/* Determine whether an object is a referenced calculation result */ -static bool_t _is_referenced_calc_type(mb_interpreter_t* s, _object_t* obj) { - mb_assert(s && obj); - - return ( -#ifdef MB_ENABLE_USERTYPE_REF - (obj->type == _DT_USERTYPE_REF) || -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_COLLECTION_LIB - (obj->type == _DT_LIST) || (obj->type == _DT_DICT) || (obj->type == _DT_LIST_IT) || (obj->type == _DT_DICT_IT) || -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - (obj->type == _DT_CLASS) || -#endif /* MB_ENABLE_CLASS */ - (obj->type == _DT_ARRAY) || - (obj->type == _DT_ROUTINE) - ); -} - -/* Calculate an expression */ -static int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val) { - int result = 0; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - register _ls_node_t* garbage = 0; - register _ls_node_t* optr = 0; - register _ls_node_t* opnd = 0; - register _object_t* c = 0; - register _object_t* x = 0; - register _object_t* a = 0; - register _object_t* b = 0; - register _object_t* r = 0; - register _object_t* theta = 0; - char pri = _ZERO_CHAR; - int* inep = 0; - int f = 0; - - _object_t* guard_val = 0; - int bracket_count = 0; - bool_t hack = false; - _ls_node_t* errn = 0; - - mb_assert(s && l); - - running = s->running_context; - ast = *l; - garbage = _ls_create(); - optr = _ls_create(); - opnd = _ls_create(); - - inep = (int*)mb_malloc(sizeof(int)); - *inep = 0; - _ls_pushback(s->in_neg_expr, inep); - - c = (_object_t*)ast->data; - do { - if(c->type == _DT_STRING) { - if(ast->next) { - _object_t* _fsn = (_object_t*)ast->next->data; - if(_IS_FUNC(_fsn, _core_add) || _IS_FUNC(_fsn, _core_and) || _IS_FUNC(_fsn, _core_or) || _IS_FUNC(_fsn, _core_is)) - break; - } - - (*val)->type = _DT_STRING; - (*val)->data.string = c->data.string; - (*val)->ref = true; - ast = ast->next; - - goto _exit; - } - } while(0); - guard_val = c; - ast = ast->next; - _ls_pushback(optr, _exp_assign); - while( - !(c->type == _DT_FUNC && strcmp(c->data.func->name, _DUMMY_ASSIGN_CHAR) == 0) || - !(((_object_t*)(_ls_back(optr)->data))->type == _DT_FUNC && strcmp(((_object_t*)(_ls_back(optr)->data))->data.func->name, _DUMMY_ASSIGN_CHAR) == 0)) { - if(!hack) { - if(_IS_FUNC(c, _core_open_bracket)) { - ++bracket_count; - } else if(_IS_FUNC(c, _core_close_bracket)) { - --bracket_count; - if(bracket_count < 0) { - c = _exp_assign; - ast = ast->prev; - - continue; - } - } - } - hack = false; - if(!(c->type == _DT_FUNC && _is_operator(c->data.func->pointer))) { - if(_is_expression_terminal(s, c)) { - c = _exp_assign; - if(ast) - ast = ast->prev; - if(bracket_count) { - _object_t _cb; - _func_t _cbf; - _MAKE_NIL(&_cb); - _cb.type = _DT_FUNC; - _cb.data.func = &_cbf; - _cb.data.func->name = ")"; - _cb.data.func->pointer = _core_close_bracket; - while(bracket_count) { - _ls_pushback(optr, &_cb); - bracket_count--; - f = 0; - } - errn = ast; - } - } else { - if(c->type == _DT_ARRAY) { - unsigned arr_idx = 0; - mb_value_u arr_val; - _data_e arr_type; - _object_t* arr_elem = 0; - -#ifdef MB_ENABLE_CLASS - if(s->last_instance) { - _ls_node_t* cs = _search_identifier_in_scope_chain(s, 0, c->data.array->name, _PATHING_NORMAL, 0, 0); - if(cs) - c = (_object_t*)cs->data; - } -#endif /* MB_ENABLE_CLASS */ -_array: - if(ast && !_IS_FUNC(((_object_t*)ast->data), _core_open_bracket)) { - _ls_pushback(opnd, c); - f++; - } else { - ast = ast->prev; - result = _get_array_index(s, &ast, c, &arr_idx, 0); - if(result != MB_FUNC_OK) { - _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - ast = ast->next; - _get_array_elem(s, c->data.array, arr_idx, &arr_val, &arr_type); - arr_elem = _create_object(); - _ls_pushback(garbage, arr_elem); - arr_elem->type = arr_type; - arr_elem->ref = true; - _COPY_BYTES(arr_elem->data.bytes, arr_val.bytes); - if(f) { - _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - _ls_pushback(opnd, arr_elem); - f++; - } - } else if(c->type == _DT_FUNC) { - ast = ast->prev; - if(_IS_UNARY_FUNC(c)) { -#ifdef MB_ENABLE_STACK_TRACE - _ls_pushback(s->stack_frames, c->data.func->name); -#endif /* MB_ENABLE_STACK_TRACE */ - result = (c->data.func->pointer)(s, (void**)&ast); -#ifdef MB_ENABLE_STACK_TRACE - _ls_popback(s->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - } else { - int calc_depth = running->calc_depth; - running->calc_depth = _INFINITY_CALC_DEPTH; -#ifdef MB_ENABLE_STACK_TRACE - _ls_pushback(s->stack_frames, c->data.func->name); -#endif /* MB_ENABLE_STACK_TRACE */ - result = (c->data.func->pointer)(s, (void**)&ast); -#ifdef MB_ENABLE_STACK_TRACE - _ls_popback(s->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - running->calc_depth = calc_depth; - } - if(result != MB_FUNC_OK) { - _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - c = _create_object(); - _ls_pushback(garbage, c); - result = _public_value_to_internal_object(&running->intermediate_value, c); - if(c->type == _DT_STRING) - c->ref = true; - if(result != MB_FUNC_OK) - goto _error; - if(f) { - _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - if(_is_array(c)) { - goto _array; - } else { - if(ast && _IS_FUNC(ast->data, _core_open_bracket)) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - } - _ls_pushback(opnd, c); - f++; - } else if(c->type == _DT_ROUTINE) { -#ifdef MB_ENABLE_CLASS - bool_t calling = false; - _object_t* obj = 0; - _ls_node_t* fn = 0; -#endif /* MB_ENABLE_CLASS */ -_routine: - ast = ast->prev; -#ifdef MB_ENABLE_CLASS - calling = s->calling; - s->calling = false; -#endif /* MB_ENABLE_CLASS */ - result = _eval_routine(s, &ast, 0, 0, c->data.routine, _has_routine_lex_arg, _pop_routine_lex_arg); -#ifdef MB_ENABLE_CLASS - s->calling = calling; -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_CLASS - obj = (_object_t*)ast->data; - if(_IS_VAR(obj) && _is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { - if(fn) { - if(ast) ast = ast->next; - obj = (_object_t*)fn->data; - if(_IS_VAR(obj)) { - c = obj; - - goto _var; - } - } - } -#endif /* MB_ENABLE_CLASS */ - if(ast) - ast = ast->prev; - if(result != MB_FUNC_OK) { - _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - c = _create_object(); - _ls_pushback(garbage, c); - result = _public_value_to_internal_object(&running->intermediate_value, c); - if(result != MB_FUNC_OK) - goto _error; - if(f) { - _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - _ls_pushback(opnd, c); - f++; - } else if(c->type == _DT_VAR && c->data.variable->data->type == _DT_ARRAY) { - unsigned arr_idx = 0; - mb_value_u arr_val; - _data_e arr_type; - _object_t* arr_elem = 0; - - if(ast && !_IS_FUNC(((_object_t*)ast->data), _core_open_bracket)) { - c = c->data.variable->data; - _ls_pushback(opnd, c); - f++; - } else { - ast = ast->prev; - result = _get_array_index(s, &ast, 0, &arr_idx, 0); - if(result != MB_FUNC_OK) { - _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - ast = ast->next; - _get_array_elem(s, c->data.variable->data->data.array, arr_idx, &arr_val, &arr_type); - arr_elem = _create_object(); - _ls_pushback(garbage, arr_elem); - arr_elem->type = arr_type; - arr_elem->ref = true; - if(arr_type == _DT_INT) { - arr_elem->data.integer = arr_val.integer; - } else if(arr_type == _DT_REAL) { - arr_elem->data.float_point = arr_val.float_point; - } else if(arr_type == _DT_STRING) { - arr_elem->data.string = arr_val.string; - } else if(arr_type == _DT_USERTYPE) { - arr_elem->data.usertype = arr_val.usertype; - } else { - mb_assert(0 && "Unsupported."); - } - if(f) { - _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - _ls_pushback(opnd, arr_elem); - f++; - } - } else { - if(c->type == _DT_VAR) { - _ls_node_t* cs = _search_identifier_in_scope_chain(s, 0, c->data.variable->name, -#ifdef MB_ENABLE_CLASS - _PU(c->data.variable->pathing), -#else /* MB_ENABLE_CLASS */ - 0, -#endif /* MB_ENABLE_CLASS */ - 0, - 0 - ); - if(cs) { - c = (_object_t*)cs->data; - if(c && c->type == _DT_VAR && c->data.variable->data->type == _DT_ROUTINE) - c = c->data.variable->data; - if(ast && ast && _IS_FUNC(ast->data, _core_open_bracket)) { - if(c && c->type == _DT_ROUTINE) - goto _routine; - } - } -#ifdef MB_ENABLE_CLASS -_var: -#endif /* MB_ENABLE_CLASS */ - if(ast) { - _object_t* _err_or_bracket = (_object_t*)ast->data; - do { -#ifdef MB_ENABLE_COLLECTION_LIB - if(_IS_VAR(c) && _IS_COLL(c->data.variable->data)) { - if(_IS_FUNC(_err_or_bracket, _core_open_bracket)) { - int_t idx = 0; - mb_value_t key; - mb_value_t ret; - _object_t* ocoll = c->data.variable->data; - - mb_make_nil(ret); - - *l = ast->prev; - - _mb_check(mb_attempt_open_bracket(s, (void**)l), _error); - - switch(ocoll->type) { - case _DT_LIST: - _mb_check(mb_pop_int(s, (void**)l, &idx), _error); - if(!_at_list(ocoll->data.list, idx, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _error, result); - } - - break; - case _DT_DICT: - mb_make_nil(key); - _mb_check(mb_pop_value(s, (void**)l, &key), _error); - if(!_find_dict(ocoll->data.dict, &key, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _error, result); - } - - break; - default: /* Do nothing */ - break; - } - - _mb_check(mb_attempt_close_bracket(s, (void**)l), _error); - - c = _create_object(); - _ls_pushback(garbage, c); - _public_value_to_internal_object(&ret, c); - - ast = *l; - } - - break; - } -#endif /* MB_ENABLE_COLLECTION_LIB */ - if(_IS_FUNC(_err_or_bracket, _core_open_bracket)) { - _handle_error_on_obj(s, SE_RN_INVALID_ID_USAGE, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - } while(0); - } - } - if(f) { - _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - _ls_pushback(opnd, c); - f++; - } - if(running->calc_depth != _INFINITY_CALC_DEPTH) - running->calc_depth--; - if(ast && (running->calc_depth == _INFINITY_CALC_DEPTH || running->calc_depth)) { - c = (_object_t*)ast->data; - if(c->type == _DT_FUNC && !_is_operator(c->data.func->pointer) && !_is_flow(c->data.func->pointer)) { - _ls_foreach(opnd, _remove_source_object); - - _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - ast = ast->next; - } else { - c = _exp_assign; - } - } - } else { - pri = _get_priority(((_object_t*)(_ls_back(optr)->data))->data.func->pointer, c->data.func->pointer); - switch(pri) { - case '<': - _ls_pushback(optr, c); - c = (_object_t*)ast->data; - ast = ast->next; - f = 0; - - break; - case '=': - x = (_object_t*)_ls_popback(optr); - c = (_object_t*)ast->data; - ast = ast->next; - - break; - case '>': - theta = (_object_t*)_ls_popback(optr); - b = (_object_t*)_ls_popback(opnd); - a = (_object_t*)_ls_popback(opnd); - r = _operate_operand(s, theta, a, b, &result); - if(!r) { - _ls_clear(optr); - _handle_error_on_obj(s, SE_RN_OPERATION_FAILED, s->source_file, errn ? DON(errn) : DON(ast), MB_FUNC_ERR, _error, result); - } - _ls_pushback(opnd, r); - _ls_pushback(garbage, r); - if(_IS_FUNC(c, _core_close_bracket)) - hack = true; - - break; - case ' ': - _handle_error_on_obj(s, SE_RN_INVALID_OPERATION_USAGE, s->source_file, errn ? DON(errn) : DON(ast), MB_FUNC_ERR, _error, result); - - break; - } - } - } - - if(errn) { - _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(errn), MB_FUNC_ERR, _error, result); - } - - c = (_object_t*)(_ls_popback(opnd)); - if(_is_unexpected_calc_type(s, c)) { - _handle_error_on_obj(s, SE_RN_INVALID_DATA_TYPE, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); - } - if(c->type == _DT_VAR) { - (*val)->type = c->data.variable->data->type; - (*val)->data = c->data.variable->data->data; - if(_is_string(c)) - (*val)->ref = true; - } else { - (*val)->type = c->type; - if(_is_string(c)) { - char* _str = _extract_string(c); - (*val)->data.string = mb_strdup(_str, strlen(_str) + 1); - (*val)->ref = false; - } else if(c->type == _DT_ARRAY) { - (*val)->data = c->data; - c->type = _DT_NIL; - } else { - (*val)->data = c->data; - } - } - if(guard_val != c && _ls_try_remove(garbage, c, _ls_cmp_data, 0)) { - _try_clear_intermediate_value(c, 0, s); - - if(_is_referenced_calc_type(s, c)) - _destroy_object_capsule_only(c, 0); - else - _destroy_object(c, 0); - } - - while(0) { -_error: - _LS_FOREACH(garbage, _do_nothing_on_object, _remove_if_exist, opnd); - } - -_exit: - _LS_FOREACH(garbage, _destroy_object, _try_clear_intermediate_value, s); - _ls_destroy(garbage); - _ls_foreach(optr, _destroy_object_not_compile_time); - _ls_foreach(opnd, _destroy_object_not_compile_time); - _ls_destroy(optr); - _ls_destroy(opnd); - *l = ast; - mb_free(_ls_popback(s->in_neg_expr)); - - return result; -} - -/* Push current variable argument list */ -static _ls_node_t* _push_var_args(mb_interpreter_t* s) { - _ls_node_t* result = s->var_args; - - s->var_args = 0; - - return result; -} - -/* Pop current variable argument list */ -static void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args) { - _ls_node_t* var_args = s->var_args; - - s->var_args = last_var_args; - if(var_args) { - _LS_FOREACH(var_args, _do_nothing_on_object, _destroy_var_arg, &s->gc); - _ls_destroy(var_args); - } -} - -/* Pop an argument from the caller or a variable argument list */ -static int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg) { - int result = MB_FUNC_OK; - _ls_node_t* ast = *l; - - mb_make_nil(*arg); - if(ast && ast->data && _IS_FUNC(ast->data, _core_args)) { - if(args) { - _object_t* obj = (_object_t*)_ls_popfront(args); - if(obj) { - _internal_object_to_public_value(obj, arg); - _destroy_object_capsule_only(obj, 0); - } - } else { - arg->type = MB_DT_UNKNOWN; - } - } else { - result = pop_arg(s, (void**)l, va, ca, ia, r, arg); - } - - return result; -} - -/* Process arguments of a routine */ -static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* parameters = 0; - mb_value_t arg; - _ls_node_t* pars = 0; - _var_t* var = 0; - _ls_node_t* rnode = 0; - unsigned ia = 0; - _ls_node_t* var_args = 0; - - parameters = r->func.basic.parameters; -#ifdef MB_ENABLE_LAMBDA - if(r->type == _IT_LAMBDA) - parameters = r->func.lambda.parameters; -#endif /* MB_ENABLE_LAMBDA */ - - if(parameters) { - mb_make_nil(arg); - pars = parameters; - pars = pars->next; - while(pars && (!has_arg || (has_arg && has_arg(s, (void**)l, va, ca, &ia, r)))) { - var = (_var_t*)pars->data; - pars = pars->next; - if(_IS_VAR_ARGS(var)) - break; - - if(pop_arg) { - mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); - } - - if(running->meta == _SCOPE_META_REF) { - _object_t* obj = (_object_t*)(_ht_find(running->var_dict, var->name)->data); - var = obj->data.variable; - - if(proc_ref) - var->data->ref = false; - } else { - rnode = _search_identifier_in_scope_chain(s, running, var->name, _PATHING_NONE, 0, 0); - if(rnode) - var = ((_object_t*)rnode->data)->data.variable; - - if(proc_ref) - var->data->ref = true; - } - - if(!pop_arg && var->data->type == _DT_STRING && !var->data->ref) - _mark_lazy_destroy_string(s, var->data->data.string); - result = _public_value_to_internal_object(&arg, var->data); - - if(result != MB_FUNC_OK) - break; - - if(args && _ls_empty(args)) - break; - } - - if(_IS_VAR_ARGS(var)) { - if(has_arg && !var_args && _IS_VAR_ARGS(var)) - var_args = s->var_args = _ls_create(); - - while(has_arg && has_arg(s, (void**)l, va, ca, &ia, r)) { - if(pop_arg) { - mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); - } - - if(var_args) { - _object_t* obj = _create_object(); - result = _public_value_to_internal_object(&arg, obj); - _ls_pushback(var_args, obj); - } - - if(args && _ls_empty(args)) - break; - } - } - - ast = *l; - if(ast) { - _object_t* obj = (_object_t*)ast->data; - if(obj && _IS_FUNC(obj, _core_args)) { - if(ast) ast = ast->next; - *l = ast; - } - } - } - - return result; -} - -/* Evaluate a routine */ -static int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { - int result = MB_FUNC_OK; -#ifdef MB_ENABLE_SOURCE_TRACE - char* src = 0; -#endif /* MB_ENABLE_SOURCE_TRACE */ -#if defined MB_ENABLE_STACK_TRACE && defined MB_ENABLE_LAMBDA - char ln[_LAMBDA_NAME_MAX_LENGTH]; -#endif /* MB_ENABLE_STACK_TRACE && MB_ENABLE_LAMBDA */ - -#ifdef MB_ENABLE_STACK_TRACE - _ls_pushback(s->stack_frames, r->name); -#endif /* MB_ENABLE_STACK_TRACE */ - -#ifdef MB_ENABLE_SOURCE_TRACE - src = s->source_file; - s->source_file = r->source_file; -#endif /* MB_ENABLE_SOURCE_TRACE */ - - if(r->type == _IT_SCRIPT && r->func.basic.entry) { - result = _eval_script_routine(s, l, va, ca, r, has_arg, pop_arg); -#ifdef MB_ENABLE_LAMBDA - } else if(r->type == _IT_LAMBDA && r->func.lambda.entry) { -# ifdef MB_ENABLE_STACK_TRACE - _ls_node_t* top = _ls_back(s->stack_frames); - if(top) { - sprintf(ln, "LAMBDA_0x%p", &r->func.lambda.ref); - top->data = ln; - } -# endif /* MB_ENABLE_STACK_TRACE */ - result = _eval_lambda_routine(s, l, va, ca, r, has_arg, pop_arg); -#endif /* MB_ENABLE_LAMBDA */ - } else if(r->type == _IT_NATIVE && r->func.native.entry) { - result = _eval_native_routine(s, l, va, ca, r, has_arg, pop_arg); - } else { - _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -_exit: -#ifdef MB_ENABLE_SOURCE_TRACE - s->source_file = src; -#endif /* MB_ENABLE_SOURCE_TRACE */ - -#ifdef MB_ENABLE_STACK_TRACE - _ls_popback(s->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - - return result; -} - -/* Evaluate a script routine */ -static int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - _routine_t* lastr = 0; - mb_value_t inte; - _ls_node_t* lastv = 0; - bool_t succ = false; -#ifdef MB_ENABLE_CLASS - bool_t pushed_inst = false; - _class_t* last_inst = 0; -#endif /* MB_ENABLE_CLASS */ - - mb_assert(s && l && r); - - if(!va && s->last_routine && !s->last_routine->func.basic.parameters && (s->last_routine->name == r->name || !strcmp(s->last_routine->name, r->name))) { - ast = *l; - _skip_to(s, &ast, 0, _DT_EOS); - if(ast && ((_object_t*)ast->data)->type == _DT_EOS) - ast = ast->next; - if(_IS_FUNC((_object_t*)ast->data, _core_enddef)) { /* Tail recursion optimization */ - *l = r->func.basic.entry; - if(*l) - *l = (*l)->next; - - goto _tail; - } - } - - lastr = s->last_routine; - s->last_routine = r; - - lastv = _push_var_args(s); - - if(!va) { - mb_check(mb_attempt_open_bracket(s, (void**)l)); - } - - running = _push_weak_scope_by_routine(s, r->func.basic.scope, r); - result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); - if(result != MB_FUNC_OK) { - if(running->meta == _SCOPE_META_REF) - _destroy_scope(s, running); - else - _pop_weak_scope(s, running); - - goto _error; - } - running = _pop_weak_scope(s, running); - - if(!va) { - _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); - } - - ast = *l; - _ls_pushback(s->sub_stack, ast); - -#ifdef MB_ENABLE_CLASS - if(r->instance && s->last_instance != r->instance) { - pushed_inst = true; - last_inst = s->last_instance; - s->last_instance = r->instance; - if(r->instance) - _push_scope_by_class(s, r->instance->scope); - } -#endif /* MB_ENABLE_CLASS */ - - running = _push_scope_by_routine(s, running); - - *l = r->func.basic.entry; - if(!(*l)) { - _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - do { - result = _execute_statement(s, l, true); - ast = *l; - if(result == MB_SUB_RETURN) { - result = MB_FUNC_OK; - - break; - } - if(result == MB_FUNC_SUSPEND && s->error_handler) { - _handle_error_now(s, SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, s->last_error_file, result); - - goto _exit; - } - if(result != MB_FUNC_OK && s->error_handler) { - if(result >= MB_EXTENDED_ABORT) - s->last_error = SE_EA_EXTENDED_ABORT; - _handle_error_now(s, s->last_error, s->last_error_file, result); - - goto _exit; - } - } while(ast); - -#ifdef MB_ENABLE_CLASS - _out_of_scope(s, running, r->instance, r, true); -#else /* MB_ENABLE_CLASS */ - _out_of_scope(s, running, 0, r, true); -#endif /* MB_ENABLE_CLASS */ - - result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); - if(result != MB_FUNC_OK) - goto _exit; - - succ = true; - - mb_make_nil(inte); - _swap_public_value(&inte, &running->intermediate_value); - - _pop_scope(s, true); - -_exit: - if(!succ) - _pop_scope(s, true); - -#ifdef MB_ENABLE_CLASS - if(pushed_inst) { - if(r->instance) - _pop_scope(s, false); - s->last_instance = last_inst; - } -#endif /* MB_ENABLE_CLASS */ - - if(succ) - _assign_public_value(&s->running_context->intermediate_value, &inte); - -_error: - s->last_routine = lastr; - - _pop_var_args(s, lastv); - -_tail: - return result; -} - -#ifdef MB_ENABLE_LAMBDA -/* Evaluate a lambda routine */ -static int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - _routine_t* lastr = 0; - mb_value_t inte; - _ls_node_t* lastv = 0; - - mb_assert(s && l && r); - - lastr = s->last_routine; - s->last_routine = r; - - lastv = _push_var_args(s); - - if(!va) { - mb_check(mb_attempt_open_bracket(s, (void**)l)); - } - - running = _link_lambda_scope_chain(s, &r->func.lambda, true); - result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); - ast = *l; - if(result != MB_FUNC_OK) { - _unlink_lambda_scope_chain(s, &r->func.lambda, true); - - goto _error; - } - running = _unlink_lambda_scope_chain(s, &r->func.lambda, true); - - if(!va) { - _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); - } - - ast = *l; - _ls_pushback(s->sub_stack, ast); - - running = _link_lambda_scope_chain(s, &r->func.lambda, false); - - *l = r->func.lambda.entry; - if(!(*l)) { - _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - do { - result = _execute_statement(s, l, true); - ast = *l; - if(result == MB_SUB_RETURN) { - result = MB_FUNC_OK; - - break; - } - if(result == MB_FUNC_SUSPEND && s->error_handler) { - _handle_error_now(s, SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, s->last_error_file, result); - - goto _exit; - } - if(result != MB_FUNC_OK && s->error_handler) { - if(result >= MB_EXTENDED_ABORT) - s->last_error = SE_EA_EXTENDED_ABORT; - _handle_error_now(s, s->last_error, s->last_error_file, result); - - goto _exit; - } - } while(ast); - - _out_of_scope(s, running, 0, r, true); - - result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); - if(result != MB_FUNC_OK) - goto _exit; - - mb_make_nil(inte); - _swap_public_value(&inte, &running->intermediate_value); - - running = _unlink_lambda_scope_chain(s, &r->func.lambda, false); - - _assign_public_value(&s->running_context->intermediate_value, &inte); - -_exit: - if(result != MB_FUNC_OK) - _unlink_lambda_scope_chain(s, &r->func.lambda, false); - -_error: - s->last_routine = lastr; - - _pop_var_args(s, lastv); - - *l = ast; - - return result; -} -#endif /* MB_ENABLE_LAMBDA */ - -/* Evaluate a native routine */ -static int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { - int result = MB_FUNC_OK; - _routine_t* lastr = 0; - mb_routine_func_t entry = 0; - _ls_node_t* lastv = 0; - - mb_assert(s && l && r); - - lastr = s->last_routine; - s->last_routine = r; - - lastv = _push_var_args(s); - - entry = r->func.native.entry; - if(!entry) { - _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - result = entry(s, (void**)l, va, ca, r, has_arg, pop_arg); - -_exit: - s->last_routine = lastr; - - _pop_var_args(s, lastv); - - return result; -} - -/* Detect if there is any more lexical argument */ -static int _has_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) { - mb_unrefvar(va); - mb_unrefvar(ca); - mb_unrefvar(ia); - mb_unrefvar(r); - - return mb_has_arg(s, l); -} - -/* Pop a lexical argument */ -static int _pop_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val) { - mb_unrefvar(va); - mb_unrefvar(ca); - mb_unrefvar(ia); - mb_unrefvar(r); - - return mb_pop_value(s, l, val); -} - -/* Detect if there is any more argument in the argument list */ -static int _has_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) { - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(va); - mb_unrefvar(r); - - return *ia < ca; -} - -/* Pop an argument from the argument list */ -static int _pop_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val) { - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(ca); - mb_unrefvar(r); - - memcpy(val, &(va[*ia]), sizeof(mb_value_t)); - (*ia)++; - - return MB_FUNC_OK; -} - -/* Determine whether an object is a PRINT termination */ -static bool_t _is_print_terminal(mb_interpreter_t* s, _object_t* obj) { - bool_t result = false; - - mb_assert(s && obj); - - result = ( - _IS_EOS(obj) || - _IS_SEP(obj, ':') || - _IS_FUNC(obj, _core_elseif) || - _IS_FUNC(obj, _core_else) || - _IS_FUNC(obj, _core_endif) - ); - - return result; -} - -/* Try to call overridden function */ -static mb_meta_status_u _try_overridden(mb_interpreter_t* s, void** l, mb_value_t* d, const char* f, mb_meta_func_u t) { - mb_assert(s && l && d && f); - -#ifdef MB_ENABLE_USERTYPE_REF - if(d->type == MB_DT_USERTYPE_REF) { - _object_t obj; - _MAKE_NIL(&obj); - _public_value_to_internal_object(d, &obj); - if(t == MB_MF_COLL && obj.data.usertype_ref->coll_func) - return obj.data.usertype_ref->coll_func(s, l, f); - else if(t == MB_MF_FUNC && obj.data.usertype_ref->generic_func) - return obj.data.usertype_ref->generic_func(s, l, f); - } -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_CLASS - if(d->type == MB_DT_CLASS) { - char buf[_TEMP_FORMAT_MAX_LENGTH]; - _ls_node_t* ofn = 0; - _object_t obj; - _MAKE_NIL(&obj); - _public_value_to_internal_object(d, &obj); - sprintf(buf, _CLASS_OVERRIDE_FMT, f); - ofn = _search_identifier_in_class(s, obj.data.instance, buf, 0, 0); - if(ofn) { - _object_t* ofo = (_object_t*)ofn->data; - _ls_node_t* ast = (_ls_node_t*)*l; - mb_value_t va[1]; - mb_make_nil(va[0]); - if(_eval_routine(s, &ast, va, 0, ofo->data.routine, _has_routine_lex_arg, _pop_routine_lex_arg) == MB_FUNC_OK) { - if(ast) - *l = ast->prev; - - return (mb_meta_status_u)(MB_MS_DONE | MB_MS_RETURNED); - } - } - } -#endif /* MB_ENABLE_CLASS */ -#if !defined MB_ENABLE_USERTYPE_REF || !defined MB_ENABLE_CLASS - mb_unrefvar(t); -#endif /* !MB_ENABLE_USERTYPE_REF && !MB_ENABLE_CLASS */ - - return MB_MS_NONE; -} - -/** Handlers */ - -/* Set current error information */ -static void _set_current_error(mb_interpreter_t* s, mb_error_e err, char* f) { - mb_assert(s && err >= 0); - - if(s->last_error == SE_NO_ERR) { - s->last_error = err; - s->last_error_file = f; - } -} - -/* Get a print functor of an interpreter */ -static mb_print_func_t _get_printer(mb_interpreter_t* s) { - mb_assert(s); - - if(s->printer) - return s->printer; - - return printf; -} - -/* Get an input functor of an interpreter */ -static mb_input_func_t _get_inputer(mb_interpreter_t* s) { - mb_assert(s); - - if(s->inputer) - return s->inputer; - - return mb_gets; -} - -/* Print a string */ -static void _print_string(mb_interpreter_t* s, _object_t* obj) { - mb_assert(s && obj); - -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE - char* loc = setlocale(LC_ALL, ""); - char* str = obj->data.string ? obj->data.string : MB_NULL_STRING; - _dynamic_buffer_t buf; - size_t lbuf = 0; - _INIT_BUF(buf); - while((lbuf = (size_t)mb_bytes_to_wchar(str, &_WCHAR_BUF_PTR(buf), _WCHARS_OF_BUF(buf))) > _WCHARS_OF_BUF(buf)) { - _RESIZE_WCHAR_BUF(buf, lbuf); - } - _get_printer(s)("%ls", _WCHAR_BUF_PTR(buf)); - _DISPOSE_BUF(buf); - setlocale(LC_ALL, loc); -#else /* MB_CP_VC && MB_ENABLE_UNICODE */ - _get_printer(s)("%s", obj->data.string ? obj->data.string : MB_NULL_STRING); -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ -} - -/** Parsing helpers */ - -/* Read all content of a file into a buffer */ -static char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix) { -#ifndef MB_CP_ARDUINO - FILE* fp = 0; - char* buf = 0; - long curpos = 0; - long l = 0; - long i = 0; - _parsing_context_t* context = 0; - - mb_assert(s); - - context = (_parsing_context_t*)s->parsing_context; - - if(_ls_find(context->imported, (void*)f, (_ls_compare)_ht_cmp_string, 0)) { - buf = (char*)f; - } else { - fp = fopen(f, "rb"); - if(fp) { - buf = mb_strdup(f, strlen(f) + 1); - _ls_pushback(context->imported, buf); - buf = 0; - - curpos = ftell(fp); - fseek(fp, 0L, SEEK_END); - l = ftell(fp); - fseek(fp, curpos, SEEK_SET); - if(prefix) { - i = (long)strlen(prefix); - l += i; - } - buf = (char*)mb_malloc((size_t)(l + 1)); - mb_assert(buf); - if(prefix) - memcpy(buf, prefix, i); - fread(buf + i, 1, l, fp); - do { - char* off = buf + i; - int b = mb_uu_getbom((const char**)&off); - if(b) { - memmove(buf + i, buf + i + b, l - b - i); - buf[l - b] = _ZERO_CHAR; - } - } while(0); - fclose(fp); - buf[l] = _ZERO_CHAR; - } - } - - return buf; -#else /* MB_CP_ARDUINO */ - return 0; -#endif /* MB_CP_ARDUINO */ -} - -/* Finish loading a file */ -static void _end_of_file(_parsing_context_t* context) { - if(context) - context->parsing_state = _PS_NORMAL; -} - -/* Determine whether a character is blank */ -static bool_t _is_blank_char(char c) { - return (c == ' ') || (c == '\t'); -} - -/* Determine whether a character is end of file */ -static bool_t _is_eof_char(char c) { - return (c == EOF); -} - -/* Determine whether a character is newline */ -static bool_t _is_newline_char(char c) { - return (c == _RETURN_CHAR) || (c == _NEWLINE_CHAR) || _is_eof_char(c); -} - -/* Determine whether a character is separator */ -static bool_t _is_separator_char(char c) { - return (c == ',') || (c == ';') || (c == ':'); -} - -/* Determine whether a character is bracket */ -static bool_t _is_bracket_char(char c) { - return (c == '(') || (c == ')'); -} - -/* Determine whether a character is quotation mark */ -static bool_t _is_quotation_char(char c) { - return (c == '"'); -} - -/* Determine whether a character is comment mark */ -static bool_t _is_comment_char(char c) { - return (c == '\''); -} - -/* Determine whether a character is accessor char */ -static bool_t _is_accessor_char(char c) { - return (c == '.'); -} - -/* Determine whether a character is numeric char */ -static bool_t _is_numeric_char(char c) { - return (c >= '0' && c <= '9') || _is_accessor_char(c); -} - -/* Determine whether a character is identifier char */ -static bool_t _is_identifier_char(char c) { - return ( - (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c == '_') || - _is_numeric_char(c) || - (c == _STRING_POSTFIX_CHAR) - ); -} - -/* Determine whether a character is operator char */ -static bool_t _is_operator_char(char c) { - return ( - (c == '+') || (c == '-') || (c == '*') || (c == '/') || - (c == '^') || - (c == '(') || (c == ')') || - (c == '=') || - (c == '>') || (c == '<') - ); -} - -/* Determine whether a character is a exponential char */ -static bool_t _is_exponential_char(char c) { - return (c == 'e') || (c == 'E'); -} - -/* Determine whether a character is module using char */ -static bool_t _is_using_char(char c) { - return (c == '@'); -} - -/* Determine whether current symbol is exponent prefix */ -static bool_t _is_exponent_prefix(char* s, int begin, int end) { - int i = 0; - - mb_assert(s); - - if(end < 0) - return false; - - for(i = begin; i <= end; i++) { - if(!_is_numeric_char(s[i])) - return false; - } - - return true; -} - -/* Parse a character and append it to current parsing symbol */ -static int _append_char_to_symbol(mb_interpreter_t* s, char c) { - int result = MB_FUNC_OK; - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - - if(_is_accessor_char(c)) - context->current_symbol_contains_accessor++; - - if(context->current_symbol_nonius + 1 >= _SINGLE_SYMBOL_MAX_LENGTH) { - _set_current_error(s, SE_PS_SYMBOL_TOO_LONG, 0); - - result = MB_FUNC_ERR; - } else { - context->current_symbol[context->current_symbol_nonius] = c; - ++context->current_symbol_nonius; - } - - return result; -} - -#ifdef MB_ENABLE_UNICODE_ID -/* Parse a UTF8 character and append it to current parsing symbol */ -static int _append_uu_char_to_symbol(mb_interpreter_t* s, const char* str, int n) { - int result = MB_FUNC_OK; - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - - if(context->current_symbol_nonius + n >= _SINGLE_SYMBOL_MAX_LENGTH) { - _set_current_error(s, SE_PS_SYMBOL_TOO_LONG, 0); - - result = MB_FUNC_ERR; - } else { - memcpy(&context->current_symbol[context->current_symbol_nonius], str, n); - context->current_symbol_nonius += n; - } - - return result; -} -#endif /* MB_ENABLE_UNICODE_ID */ - -/* Cut current symbol when current one parsing is finished */ -static int _cut_symbol(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) { - int result = MB_FUNC_OK; - _parsing_context_t* context = 0; - char* sym = 0; - int status = 0; - bool_t delsym = false; - - mb_assert(s); - - context = s->parsing_context; - if(context->current_symbol_nonius && context->current_symbol[0] != _ZERO_CHAR) { - sym = (char*)mb_malloc(context->current_symbol_nonius + 1); - memcpy(sym, context->current_symbol, context->current_symbol_nonius + 1); - - status = _append_symbol(s, sym, &delsym, pos, row, col); - if(status || delsym) { - safe_free(sym); - } - result = status; - } - memset(context->current_symbol, 0, sizeof(context->current_symbol)); - context->current_symbol_nonius = 0; - context->current_symbol_contains_accessor = 0; - - return result; -} - -/* Append cut symbol to the AST list */ -static int _append_symbol(mb_interpreter_t* s, char* sym, bool_t* delsym, int pos, unsigned short row, unsigned short col) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _ls_node_t** assign = 0; - _ls_node_t* node = 0; - _parsing_context_t* context = 0; - - mb_assert(s && sym); - - ast = s->ast; - result = _create_symbol(s, ast, sym, &obj, &assign, delsym); - if(obj) { -#ifdef MB_ENABLE_SOURCE_TRACE - obj->source_pos = pos; - obj->source_row = row; - obj->source_col = col; -#else /* MB_ENABLE_SOURCE_TRACE */ - mb_unrefvar(row); - mb_unrefvar(col); - - obj->source_pos = (char)pos; -#endif /* MB_ENABLE_SOURCE_TRACE */ - - node = _ls_pushback(ast, obj); - if(assign) - *assign = node; - - context = s->parsing_context; - context->last_symbol = obj; - } - - return result; -} - -/* Create a syntax symbol */ -static int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** obj, _ls_node_t*** asgn, bool_t* delsym) { - int result = MB_FUNC_OK; - _data_e type; - union { - _func_t* func; _array_t* array; -#ifdef MB_ENABLE_CLASS - _class_t* instance; -#endif /* MB_ENABLE_CLASS */ - _routine_t* routine; _var_t* var; _label_t* label; real_t float_point; int_t integer; _raw_t any; - } tmp; - _raw_t value; - unsigned ul = 0; - _parsing_context_t* context = 0; - _running_context_t* running = 0; - _ls_node_t* glbsyminscope = 0; - bool_t is_field = false; - mb_unrefvar(l); - - mb_assert(s && sym && obj); - - memset(value, 0, sizeof(_raw_t)); - - context = s->parsing_context; - running = s->running_context; - - *obj = _create_object(); -#ifdef MB_ENABLE_SOURCE_TRACE - (*obj)->source_pos = -1; - (*obj)->source_row = (*obj)->source_col = 0xFFFF; -#else /* MB_ENABLE_SOURCE_TRACE */ - (*obj)->source_pos = -1; -#endif /* MB_ENABLE_SOURCE_TRACE */ - - type = _get_symbol_type(s, sym, &value); - if(s->last_error != SE_NO_ERR) { - result = MB_FUNC_ERR; - - goto _exit; - } - (*obj)->type = type; - switch(type) { - case _DT_NIL: - memcpy(tmp.any, value, sizeof(_raw_t)); - if(tmp.integer) { /* Nil type */ - (*obj)->type = _DT_NIL; - } else { /* End of line character */ - safe_free(*obj); - } - safe_free(sym); - - break; - case _DT_INT: - memcpy(tmp.any, value, sizeof(_raw_t)); - (*obj)->data.integer = tmp.integer; - safe_free(sym); - - break; - case _DT_REAL: - memcpy(tmp.any, value, sizeof(_raw_t)); - (*obj)->data.float_point = tmp.float_point; - safe_free(sym); - - break; - case _DT_STRING: { - size_t _sl = strlen(sym); - (*obj)->data.string = (char*)mb_malloc(_sl - 2 + 1); - memcpy((*obj)->data.string, sym + sizeof(char), _sl - 2); - (*obj)->data.string[_sl - 2] = _ZERO_CHAR; - *delsym = true; - } - - break; - case _DT_FUNC: - tmp.func = (_func_t*)mb_malloc(sizeof(_func_t)); - memset(tmp.func, 0, sizeof(_func_t)); - tmp.func->name = sym; - memcpy(&tmp.func->pointer, value, sizeof(tmp.func->pointer)); - (*obj)->data.func = tmp.func; - - break; - case _DT_ARRAY: - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ARRAY) { - (*obj)->data.array = ((_object_t*)glbsyminscope->data)->data.array; - (*obj)->ref = true; - *delsym = true; - } else { - tmp.array = _create_array(s, sym, _DT_UNKNOWN); - memcpy(&tmp.array->type, value, sizeof(tmp.array->type)); - (*obj)->data.array = tmp.array; - - ul = _ht_set_or_insert(running->var_dict, sym, *obj); - mb_assert(ul); - - *obj = _create_object(); - (*obj)->type = type; - (*obj)->data.array = tmp.array; - (*obj)->ref = true; - } - - break; -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { - (*obj)->data.instance = ((_object_t*)glbsyminscope->data)->data.instance; - (*obj)->ref = true; - *delsym = true; - if(running != (*obj)->data.instance->scope && - (context->class_state != _CLASS_STATE_NONE) && - _IS_FUNC(context->last_symbol, _core_class)) { - _push_scope_by_class(s, (*obj)->data.instance->scope); - } - } else { - _var_t* var = 0; - - tmp.instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_class(s, tmp.instance, sym); - _push_scope_by_class(s, tmp.instance->scope); - s->last_instance = tmp.instance; - - mb_unrefvar(var); - - (*obj)->data.instance = tmp.instance; - - ul = _ht_set_or_insert(running->var_dict, sym, *obj); - mb_assert(ul); - - *obj = _create_object(); - (*obj)->type = type; - (*obj)->data.instance = tmp.instance; - (*obj)->ref = true; - } - - break; -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ROUTINE) { - (*obj)->data.routine = ((_object_t*)glbsyminscope->data)->data.routine; - (*obj)->ref = true; - *delsym = true; - if(running != (*obj)->data.routine->func.basic.scope && - context->routine_state && - _IS_FUNC(context->last_symbol, _core_def)) { - _push_scope_by_routine(s, (*obj)->data.routine->func.basic.scope); - } - } else { - _running_context_t* tba = 0; - tmp.routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); - _init_routine(s, tmp.routine, sym, 0); - _push_scope_by_routine(s, tmp.routine->func.basic.scope); - (*obj)->data.routine = tmp.routine; - - tba = _get_scope_to_add_routine(s); - ul = _ht_set_or_insert(tba->var_dict, sym, *obj); - mb_assert(ul); - if(tba != _OUTTER_SCOPE(running) && tba != running) - _pop_scope(s, false); - - *obj = _create_object(); - (*obj)->type = type; - (*obj)->data.routine = tmp.routine; - (*obj)->ref = true; - -#ifdef MB_ENABLE_CLASS - tmp.routine->instance = s->last_instance; -#endif /* MB_ENABLE_CLASS */ - } - - break; - case _DT_VAR: - if(context->routine_params_state) - glbsyminscope = _ht_find(running->var_dict, sym); - else - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); -#ifdef MB_ENABLE_CLASS - is_field = context->last_symbol && _IS_FUNC(context->last_symbol, _core_var); -#endif /* MB_ENABLE_CLASS */ - if(!is_field && glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { - (*obj)->data.variable = ((_object_t*)glbsyminscope->data)->data.variable; - (*obj)->ref = true; - *delsym = true; - } else { -#ifdef MB_ENABLE_CLASS - if(strcmp(sym, _CLASS_ME) == 0) { - _handle_error_now(s, SE_RN_CANNOT_CHANGE_ME, s->source_file, MB_FUNC_ERR); - (*obj)->ref = true; - *delsym = true; - - goto _exit; - } -#endif /* MB_ENABLE_CLASS */ - tmp.var = (_var_t*)mb_malloc(sizeof(_var_t)); - memset(tmp.var, 0, sizeof(_var_t)); - tmp.var->name = sym; - tmp.var->data = _create_object(); - tmp.var->data->type = (sym[strlen(sym) - 1] == _STRING_POSTFIX_CHAR) ? _DT_STRING : _DT_INT; - tmp.var->data->data.integer = 0; -#ifdef MB_ENABLE_CLASS - if(context->class_state != _CLASS_STATE_NONE) - tmp.var->pathing = true; - else if(!is_field) - tmp.var->pathing = !!context->current_symbol_contains_accessor; -#endif /* MB_ENABLE_CLASS */ - (*obj)->data.variable = tmp.var; - - ul = _ht_set_or_insert(running->var_dict, sym, *obj); - mb_assert(ul); - - *obj = _create_object(); - (*obj)->type = type; - (*obj)->data.variable = tmp.var; - (*obj)->ref = true; - } - - break; - case _DT_LABEL: - if(context->current_char == ':') { - if(mb_memtest(value, sizeof(_raw_t))) { - memcpy(&((*obj)->data.label), value, sizeof((*obj)->data.label)); - (*obj)->ref = true; - *delsym = true; - } else { - tmp.label = (_label_t*)mb_malloc(sizeof(_label_t)); - memset(tmp.label, 0, sizeof(_label_t)); - tmp.label->name = sym; - *asgn = &(tmp.label->node); - (*obj)->data.label = tmp.label; - - ul = _ht_set_or_insert(running->var_dict, sym, *obj); - mb_assert(ul); - - *obj = _create_object(); - (*obj)->type = type; - (*obj)->data.label = tmp.label; - (*obj)->ref = true; - } - } else { - (*obj)->data.label = (_label_t*)mb_malloc(sizeof(_label_t)); - memset((*obj)->data.label, 0, sizeof(_label_t)); - (*obj)->data.label->name = sym; - } - - break; - case _DT_SEP: - (*obj)->data.separator = sym[0]; - safe_free(sym); - - break; - case _DT_EOS: - safe_free(sym); - - break; - default: /* Do nothing */ - break; - } - -_exit: - return result; -} - -/* Get the type of a syntax symbol */ -static _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) { - _data_e result = _DT_NIL; - union { real_t float_point; int_t integer; _object_t* obj; _raw_t any; } tmp; - char* conv_suc = 0; - _parsing_context_t* context = 0; - _running_context_t* running = 0; - _ls_node_t* glbsyminscope = 0; - size_t _sl = 0; - _data_e en = _DT_UNKNOWN; - intptr_t ptr = 0; - bool_t mod = false; - - mb_assert(s && sym); - _sl = strlen(sym); - mb_assert(_sl > 0); - - context = s->parsing_context; - running = s->running_context; - - /* int_t */ - tmp.integer = (int_t)mb_strtol(sym, &conv_suc, 0); - if(*conv_suc == _ZERO_CHAR) { - memcpy(*value, tmp.any, sizeof(_raw_t)); - - result = _DT_INT; - - goto _exit; - } - /* real_t */ - tmp.float_point = (real_t)mb_strtod(sym, &conv_suc); - if(*conv_suc == _ZERO_CHAR) { - memcpy(*value, tmp.any, sizeof(_raw_t)); - - result = _DT_REAL; - - goto _exit; - } - /* String */ - if(_is_quotation_char(sym[0]) && _is_quotation_char(sym[_sl - 1]) && _sl >= 2) { - result = _DT_STRING; - - if(context->last_symbol && _IS_FUNC(context->last_symbol, _core_import)) { - /* IMPORT statement */ - int n = context->current_symbol_nonius; - char current_symbol[_SINGLE_SYMBOL_MAX_LENGTH + 1]; - char* buf = 0; - memcpy(current_symbol, context->current_symbol, sizeof(current_symbol)); - memset(context->current_symbol, 0, sizeof(current_symbol)); - context->current_symbol_nonius = 0; - context->last_symbol = 0; - sym[_sl - 1] = _ZERO_CHAR; - context->parsing_state = _PS_NORMAL; - /* Using a module */ - if(_is_using_char(*(sym + 1))) { -#ifdef MB_ENABLE_MODULE - char* ns = mb_strdup(sym + 2, strlen(sym + 2) + 1); - if(_ls_find(s->using_modules, ns, (_ls_compare)_ht_cmp_string, 0)) { - safe_free(ns); - } else { - _ls_pushback(s->using_modules, ns); - } - - goto _end_import; -#else /* MB_ENABLE_MODULE */ - _handle_error_now(s, SE_CM_NOT_SUPPORTED, s->source_file, MB_FUNC_ERR); - - goto _end_import; -#endif /* MB_ENABLE_MODULE */ - } - /* Import another file */ - buf = _load_file(s, sym + 1, ":"); - if(buf) { - if(buf != sym + 1) { - char* lf = (char*)(_ls_back(context->imported)->data); - int pos; unsigned short row, col; - lf = _prev_import(s, lf, &pos, &row, &col); - mb_load_string(s, buf, true); - safe_free(buf); - _post_import(s, lf, &pos, &row, &col); - } - } else { - if(!_ls_find(context->imported, (void*)(sym + 1), (_ls_compare)_ht_cmp_string, 0)) { - if(s->import_handler) { - _object_t* sep = _create_object(); - sep->type = _DT_SEP; - sep->data.separator = ':'; - _ls_pushback(s->ast, sep); - if(s->import_handler(s, sym + 1) == MB_FUNC_OK) { - _ls_pushback(context->imported, mb_strdup(sym + 1, strlen(sym + 1) + 1)); - } else { - _handle_error_now(s, SE_PS_OPEN_FILE_FAILED, s->source_file, MB_FUNC_ERR); - } - } else { - _handle_error_now(s, SE_PS_OPEN_FILE_FAILED, s->source_file, MB_FUNC_ERR); - } - } - } - -_end_import: - context->parsing_state = _PS_STRING; - sym[_sl - 1] = '"'; - context->current_symbol_nonius = n; - memcpy(context->current_symbol, current_symbol, sizeof(current_symbol)); - result = _DT_NIL; - } - - goto _exit; - } - /* Nil */ - if(!strcmp(sym, MB_NIL)) { - tmp.integer = ~0; - memcpy(*value, tmp.any, sizeof(_raw_t)); - - result = _DT_NIL; - - goto _exit; - } - /* REM */ - if(!strcmp(sym, _REMARK_STR)) { - context->parsing_state = _PS_COMMENT; - - result = _DT_EOS; - - goto _exit; - } - /* _array_t */ - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ARRAY) { - tmp.obj = (_object_t*)glbsyminscope->data; - memcpy(*value, &(tmp.obj->data.array->type), sizeof(tmp.obj->data.array->type)); - - result = _DT_ARRAY; - - goto _exit; - } - if(context->last_symbol && _IS_FUNC(context->last_symbol, _core_dim)) { -#ifdef MB_SIMPLE_ARRAY - en = (sym[_sl - 1] == _STRING_POSTFIX_CHAR ? _DT_STRING : _DT_REAL); -#else /* MB_SIMPLE_ARRAY */ - en = _DT_REAL; -#endif /* MB_SIMPLE_ARRAY */ - memcpy(*value, &en, sizeof(en)); - - result = _DT_ARRAY; - - goto _exit; - } - /* _class_t */ -#ifdef MB_ENABLE_CLASS - if(context->last_symbol) { - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { - if(_IS_FUNC(context->last_symbol, _core_class)) { - _handle_error_now(s, SE_RN_DUPLICATE_CLASS, s->source_file, MB_FUNC_ERR); - } - result = _DT_CLASS; - - goto _exit; - } - if(_IS_FUNC(context->last_symbol, _core_class)) { - if(s->last_instance) { - _handle_error_now(s, SE_RN_DUPLICATE_CLASS, s->source_file, MB_FUNC_ERR); - - goto _exit; - } - _begin_class(s); -# ifdef MB_ENABLE_UNICODE_ID - if(!_is_identifier_char(sym[0]) && !mb_uu_ischar(sym)) { -# else /* MB_ENABLE_UNICODE_ID */ - if(!_is_identifier_char(sym[0])) { -# endif /* MB_ENABLE_UNICODE_ID */ - result = _DT_NIL; - - goto _exit; - } - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { - _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); - - goto _exit; - } - - if(context->routine_state > 1) { - _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); - - goto _exit; - } - - result = _DT_CLASS; - - goto _exit; - } else if(_IS_FUNC(context->last_symbol, _core_endclass)) { - if(_end_class(s)) - _pop_scope(s, false); - } - } -#endif /* MB_ENABLE_CLASS */ - /* _routine_t */ - if(context->last_symbol && !_is_bracket_char(sym[0])) { - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ROUTINE) { - if(_IS_FUNC(context->last_symbol, _core_def)) { - if(_begin_routine(s) != MB_FUNC_OK) - goto _exit; - } - result = _DT_ROUTINE; - - goto _exit; - } - if(_IS_FUNC(context->last_symbol, _core_def) || _IS_FUNC(context->last_symbol, _core_call)) { - if(_IS_FUNC(context->last_symbol, _core_def)) { - if(_begin_routine(s) != MB_FUNC_OK) - goto _exit; - } -#ifdef MB_ENABLE_UNICODE_ID - if(!_is_identifier_char(sym[0]) && !mb_uu_ischar(sym)) { -#else /* MB_ENABLE_UNICODE_ID */ - if(!_is_identifier_char(sym[0])) { -#endif /* MB_ENABLE_UNICODE_ID */ - result = _DT_NIL; - - goto _exit; - } - if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { - _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); - - goto _exit; - } - - if(_IS_FUNC(context->last_symbol, _core_def)) { - if(context->routine_state > 1) { - _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); - - goto _exit; - } - } - - result = _DT_ROUTINE; - - goto _exit; - } else if(_IS_FUNC(context->last_symbol, _core_enddef)) { - if(_end_routine(s)) - _pop_scope(s, false); - } - } - /* _func_t */ - 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) { - ptr = (intptr_t)_core_neg; - memcpy(*value, &ptr, sizeof(intptr_t)); - - result = _DT_FUNC; - - goto _exit; - } - } - glbsyminscope = _find_func(s, sym, &mod); - if(glbsyminscope) { - if(context->last_symbol && context->last_symbol->type == _DT_ROUTINE) { - if(_sl == 1 && sym[0] == '(') - _begin_routine_parameter_list(s); - } - if(context->routine_params_state) { - if(_sl == 1 && sym[0] == ')') - _end_routine_parameter_list(s); - } - -#ifdef MB_ENABLE_MODULE - if(mod) { - _module_func_t* mp = (_module_func_t*)glbsyminscope->data; - ptr = (intptr_t)mp->func; - memcpy(*value, &ptr, sizeof(intptr_t)); - } else { - ptr = (intptr_t)glbsyminscope->data; - memcpy(*value, &ptr, sizeof(intptr_t)); - } -#else /* MB_ENABLE_MODULE */ - ptr = (intptr_t)glbsyminscope->data; - memcpy(*value, &ptr, sizeof(intptr_t)); -#endif /* MB_ENABLE_MODULE */ - - result = _DT_FUNC; - - goto _exit; - } - /* MB_EOS */ - if(_sl == 1 && sym[0] == MB_EOS) { - if(_IS_EOS(context->last_symbol)) - result = _DT_NIL; - else - result = _DT_EOS; - - goto _exit; - } - /* Separator */ - if(_sl == 1 && _is_separator_char(sym[0])) { - result = _DT_SEP; - - goto _exit; - } - /* _var_t */ - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope) { - if(((_object_t*)glbsyminscope->data)->type != _DT_LABEL) { - memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); - - result = _DT_VAR; - - goto _exit; - } - } - /* _label_t */ - if(context->current_char == ':') { - if(!context->last_symbol || _IS_EOS(context->last_symbol)) { - glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); - if(glbsyminscope) - memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); - - result = _DT_LABEL; - - goto _exit; - } - } - if(context->last_symbol && (_IS_FUNC(context->last_symbol, _core_goto) || _IS_FUNC(context->last_symbol, _core_gosub))) { - result = _DT_LABEL; - - goto _exit; - } - /* Otherwise */ - result = _DT_VAR; - -_exit: - return result; -} - -/* Parse a character */ -static int _parse_char(mb_interpreter_t* s, const char* str, int n, int pos, unsigned short row, unsigned short col) { - int result = MB_FUNC_OK; - _parsing_context_t* context = 0; - char last_char = _ZERO_CHAR; - char c = '\0'; -#ifdef MB_ENABLE_UNICODE_ID - unsigned uc = 0; -#else /* MB_ENABLE_UNICODE_ID */ - mb_unrefvar(n); -#endif /* MB_ENABLE_UNICODE_ID */ - - mb_assert(s && s->parsing_context); - - context = s->parsing_context; - - if(str) { -#ifdef MB_ENABLE_UNICODE_ID - if(n == 1) - c = *str; - else - memcpy(&uc, str, n); -#else /* MB_ENABLE_UNICODE_ID */ - c = *str; -#endif /* MB_ENABLE_UNICODE_ID */ - } else { - c = MB_EOS; - } - - last_char = context->current_char; - context->current_char = c; - - switch(context->parsing_state) { - case _PS_NORMAL: -#ifdef MB_ENABLE_UNICODE_ID - if(uc) { - if(context->symbol_state == _SS_IDENTIFIER) { - _mb_check(result = _append_uu_char_to_symbol(s, str, n), _exit); - } else if(context->symbol_state == _SS_OPERATOR) { - context->symbol_state = _SS_IDENTIFIER; - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_uu_char_to_symbol(s, str, n), _exit); - } - - break; - } -#endif /* MB_ENABLE_UNICODE_ID */ - - c = toupper(c); - if(_is_blank_char(c)) { /* \t space */ - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - } else if(_is_newline_char(c)) { /* \r \n EOF */ - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, MB_EOS), _exit); - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - } else if(_is_separator_char(c) || _is_bracket_char(c)) { /* , ; : ( ) */ - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, c), _exit); - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - } else if(_is_quotation_char(c)) { /* " */ - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, c), _exit); - context->parsing_state = _PS_STRING; - } else if(_is_comment_char(c)) { /* ' */ - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, MB_EOS), _exit); - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - context->parsing_state = _PS_COMMENT; - context->multi_line_comment_count = 1; - } else { - if(context->symbol_state == _SS_IDENTIFIER) { - if(_is_identifier_char(c)) { - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } else if(_is_operator_char(c)) { - if(_is_exponent_prefix(context->current_symbol, 0, context->current_symbol_nonius - 2) && _is_exponential_char(last_char) && c == '-') { - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } else { - context->symbol_state = _SS_OPERATOR; - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } - } else { - _handle_error_at_pos(s, SE_PS_INVALID_CHAR, s->source_file, pos, row, col, MB_FUNC_ERR, _exit, result); - } - } else if(context->symbol_state == _SS_OPERATOR) { - if(_is_identifier_char(c)) { - context->symbol_state = _SS_IDENTIFIER; - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } else if(_is_operator_char(c)) { - if(c == '-') - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } else { - _handle_error_at_pos(s, SE_PS_INVALID_CHAR, s->source_file, pos, row, col, MB_FUNC_ERR, _exit, result); - } - } else { - mb_assert(0 && "Impossible."); - } - } - - break; - case _PS_STRING: - if(_is_quotation_char(c)) { /* " */ - _mb_check(result = _append_char_to_symbol(s, c), _exit); - _mb_check(result = _cut_symbol(s, pos, row, col), _exit); - context->parsing_state = _PS_NORMAL; - } else { - _mb_check(result = _append_char_to_symbol(s, c), _exit); - } - - break; - case _PS_COMMENT: - if(_is_eof_char(c)) { /* EOF */ - context->parsing_state = _PS_NORMAL; - - break; - } - if(context->multi_line_comment_count != 0 && c == _MULTI_LINE_COMMENT_PREFIX[context->multi_line_comment_count++]) { - if(context->multi_line_comment_count >= countof(_MULTI_LINE_COMMENT_PREFIX) - 1) { - context->parsing_state = _PS_MULTI_LINE_COMMENT; - context->multi_line_comment_count = 0; - - break; - } - - break; - } else { - context->multi_line_comment_count = 0; - } - if(_is_newline_char(c)) /* \r \n EOF */ - context->parsing_state = _PS_NORMAL; - - break; - case _PS_MULTI_LINE_COMMENT: - if(_is_eof_char(c)) { /* EOF */ - context->parsing_state = _PS_NORMAL; - - break; - } - if(_is_comment_char(c) && context->multi_line_comment_count == 0) { - context->multi_line_comment_count = 1; - } else if(context->multi_line_comment_count != 0 && c == _MULTI_LINE_COMMENT_POSTFIX[context->multi_line_comment_count++]) { - if(context->multi_line_comment_count >= countof(_MULTI_LINE_COMMENT_POSTFIX) - 1) { - context->parsing_state = _PS_NORMAL; - context->multi_line_comment_count = 0; - } - } else { - context->multi_line_comment_count = 0; - } - - break; - default: - mb_assert(0 && "Unknown parsing state."); - - break; - } - -_exit: - return result; -} - -/* Set the position of an error */ -static void _set_error_pos(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) { - mb_assert(s); - - s->last_error_pos = pos; - s->last_error_row = row; - s->last_error_col = col; -} - -/* Do something before importing another file */ -static char* _prev_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) { -#ifdef MB_ENABLE_SOURCE_TRACE - char* result = 0; - _parsing_context_t* context = 0; - _import_info_t* info = 0; - _object_t* obj = 0; - - mb_assert(s); - - context = s->parsing_context; - if(context) { - if(pos) *pos = context->parsing_pos; - if(row) *row = context->parsing_row; - if(col) *col = context->parsing_col; - context->parsing_pos = 0; - context->parsing_row = 1; - context->parsing_col = 0; - } - - result = s->source_file; - s->source_file = lf; - - obj = _create_object(); - obj->type = _DT_EOS; - obj->ref = false; - _ls_pushback(s->ast, obj); - - info = (_import_info_t*)mb_malloc(sizeof(_import_info_t)); - info->source_file = lf ? mb_strdup(lf, strlen(lf) + 1) : 0; - obj = _create_object(); - obj->type = _DT_PREV_IMPORT; - obj->ref = false; - obj->data.import_info = info; - _ls_pushback(s->ast, obj); - - return result; -#else /* MB_ENABLE_SOURCE_TRACE */ - mb_unrefvar(s); - mb_unrefvar(lf); - mb_unrefvar(pos); - mb_unrefvar(row); - mb_unrefvar(col); - - return 0; -#endif /* MB_ENABLE_SOURCE_TRACE */ -} - -/* Do something after importing another file */ -static char* _post_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) { -#ifdef MB_ENABLE_SOURCE_TRACE - char* result = 0; - _parsing_context_t* context = 0; - _import_info_t* info = 0; - _object_t* obj = 0; - - mb_assert(s); - - context = s->parsing_context; - if(context) { - if(pos) context->parsing_pos = *pos; - if(row) context->parsing_row = *row; - if(col) context->parsing_col = *col; - } - - s->source_file = lf; - result = s->source_file; - - info = (_import_info_t*)mb_malloc(sizeof(_import_info_t)); - info->source_file = lf ? mb_strdup(lf, strlen(lf) + 1) : 0; - obj = _create_object(); - obj->type = _DT_POST_IMPORT; - obj->ref = false; - obj->data.import_info = info; - _ls_pushback(s->ast, obj); - - return result; -#else /* MB_ENABLE_SOURCE_TRACE */ - mb_unrefvar(s); - mb_unrefvar(lf); - mb_unrefvar(pos); - mb_unrefvar(row); - mb_unrefvar(col); - - return 0; -#endif /* MB_ENABLE_SOURCE_TRACE */ -} - -/** Object processors */ - -/* Get the size of a data type */ -static int_t _get_size_of(_data_e type) { - int_t result = 0; - -#ifdef MB_SIMPLE_ARRAY - if(type == _DT_INT) { - result = sizeof(int_t); - } else if(type == _DT_REAL) { - result = sizeof(real_t); - } else if(type == _DT_STRING) { - result = sizeof(char*); - } else { - mb_assert(0 && "Unsupported."); - } -#else /* MB_SIMPLE_ARRAY */ - mb_unrefvar(type); - - result = sizeof(_raw_u); -#endif /* MB_SIMPLE_ARRAY */ - - return result; -} - -/* Try to get a value (typed as int_t, real_t or char*) */ -static bool_t _try_get_value(_object_t* obj, mb_value_u* val, _data_e expected) { - bool_t result = false; - - mb_assert(obj && val); - - if(obj->type == _DT_INT && (expected == _DT_UNKNOWN || expected == _DT_INT)) { - val->integer = obj->data.integer; - result = true; - } else if(obj->type == _DT_REAL && (expected == _DT_UNKNOWN || expected == _DT_REAL)) { - val->float_point = obj->data.float_point; - result = true; - } else if(obj->type == _DT_VAR) { - result = _try_get_value(obj->data.variable->data, val, expected); - } - - return result; -} - -/* Determine if an object is a number */ -static bool_t _is_number(void* obj) { - bool_t result = false; - _object_t* o = 0; - - mb_assert(obj); - - o = (_object_t*)obj; - if(o->type == _DT_INT || o->type == _DT_REAL) - result = true; - else if(o->type == _DT_VAR) - result = o->data.variable->data->type == _DT_INT || o->data.variable->data->type == _DT_REAL; - - return result; -} - -/* Determine if an object is a string value or a string variable */ -static bool_t _is_string(void* obj) { - bool_t result = false; - _object_t* o = 0; - - mb_assert(obj); - - o = (_object_t*)obj; - if(o->type == _DT_STRING) - result = true; - else if(o->type == _DT_VAR) - result = o->data.variable->data->type == _DT_STRING; - - return result; -} - -/* Extract a string from an object */ -static char* _extract_string(_object_t* obj) { - char* result = 0; - - mb_assert(obj); - - if(obj->type == _DT_STRING) - result = obj->data.string; - else if(obj->type == _DT_VAR && obj->data.variable->data->type == _DT_STRING) - result = obj->data.variable->data->data.string; - - if(!result) - result = MB_NULL_STRING; - - return result; -} - -#ifdef _HAS_REF_OBJ_LOCK -/* Lock a referenced object */ -static bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { - mb_assert(lk); - - _ref(ref, obj); - if(*lk >= 0) - ++(*lk); - else - --(*lk); - - return true; -} - -/* Unlock a referenced object */ -static bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { - bool_t result = true; - - mb_assert(lk); - - if(*lk > 0) - --(*lk); - else if(*lk < 0) - ++(*lk); - else - result = false; - _unref(ref, obj); - - return result; -} - -/* Write operation on a referenced object */ -static bool_t _write_on_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { - bool_t result = true; - mb_unrefvar(ref); - mb_unrefvar(obj); - - mb_assert(lk); - - if(*lk > 0) - *lk = -(*lk); - else - result = false; - - return result; -} -#endif /* _HAS_REF_OBJ_LOCK */ - -/* Increase the reference of a stub by 1 */ -static _ref_count_t _ref(_ref_t* ref, void* data) { - _ref_count_t before = *ref->count; - mb_unrefvar(data); - - ++(*ref->count); - if(before > *ref->count) { - mb_assert(0 && "Too many referencing, count overflow, please redefine _ref_count_t."); - - _handle_error_now(ref->s, SE_RN_REFERENCE_COUNT_OVERFLOW, ref->s->last_error_file, MB_FUNC_ERR); - } - - return *ref->count; -} - -/* Decrease the reference of a stub by 1 */ -static bool_t _unref(_ref_t* ref, void* data) { - bool_t result = true; - _gc_t* gc = 0; - bool_t cld = false; - - cld = *ref->count == _NONE_REF + 1; - do { - gc = &ref->s->gc; - result = --(*ref->count) == _NONE_REF; - mb_assert(*ref->count >= _NONE_REF); - _gc_add(ref, data, &ref->s->gc); - if(ref->count && *ref->count == _NONE_REF) - _tidy_intermediate_value(ref, data); - ref->on_unref(ref, data); - if(result) - _gc_remove(ref, data, gc); - } while(0); - if(cld) { - _ht_set_or_insert(gc->collected_table, ref, data); - _ht_set_or_insert(gc->collected_table, data, ref); - } - - return result; -} - -/* Increase the weak reference of a stub by 1 */ -static _ref_count_t _weak_ref(_ref_t* ref, void* data, _ref_t* weak) { - _ref_count_t before = *ref->weak_count; - mb_unrefvar(data); - - ++(*ref->weak_count); - if(before > *ref->weak_count) { - mb_assert(0 && "Too many referencing, weak count overflow, please redefine _ref_count_t."); - - _handle_error_now(ref->s, SE_RN_WEAK_REFERENCE_COUNT_OVERFLOW, ref->s->last_error_file, MB_FUNC_ERR); - } - memcpy(weak, ref, sizeof(_ref_t)); - - return *ref->weak_count; -} - -/* Decrease the weak reference of a stub by 1 */ -static bool_t _weak_unref(_ref_t* weak) { - bool_t result = true; - - --(*weak->weak_count); - mb_assert(*weak->weak_count >= _NONE_REF); - if(weak->count && *weak->count == _NONE_REF) - result = false; - if(weak->count && *weak->count == _NONE_REF && weak->weak_count && *weak->weak_count == _NONE_REF) { - safe_free(weak->weak_count); - safe_free(weak->count); - } - - return result; -} - -/* Create a reference stub, initialize the reference count with zero */ -static void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpreter_t* s) { - if(ref->count) - return; - - ref->count = (_ref_count_t*)mb_malloc(sizeof(_ref_count_t)); - *ref->count = _NONE_REF; - ref->weak_count = (_ref_count_t*)mb_malloc(sizeof(_ref_count_t)); - *ref->weak_count = _NONE_REF; - ref->on_unref = dtor; - ref->type = t; - ref->s = s; -} - -/* Destroy a reference stub */ -static void _destroy_ref(_ref_t* ref) { - if(!ref->count || !ref->weak_count) - return; - - if(*ref->weak_count == _NONE_REF) { - safe_free(ref->weak_count); - safe_free(ref->count); - } - ref->on_unref = 0; -} - -/* Add a referenced object to GC table for later garbage detection */ -static void _gc_add(_ref_t* ref, void* data, _gc_t* gc) { - _ht_node_t* table = 0; - - mb_assert(ref && data); - - if(!ref->count) - return; - - if(gc && _ht_find(gc->collected_table, ref)) - _ht_remove(gc->collected_table, ref, 0); - - if(!gc->table) - return; - - if(gc->collecting) - table = gc->recursive_table; - else - table = gc->table; - - if(gc && gc->valid_table && _ht_find(gc->valid_table, ref)) - _ht_remove(table, ref, 0); - else if(ref->count && *ref->count > _NONE_REF) - _ht_set_or_insert(table, ref, data); - else - _ht_remove(table, ref, 0); -} - -/* Remove a referenced object from GC */ -static void _gc_remove(_ref_t* ref, void* data, _gc_t* gc) { - _ht_node_t* table = 0; - - mb_assert(ref && data && gc); - - if(gc->collecting) - table = gc->recursive_table; - else - table = gc->table; - - if(table) - _ht_remove(table, ref, 0); -} - -/* Get reachable objects */ -static int _gc_add_reachable(void* data, void* extra, void* h) { - int result = _OP_RESULT_NORMAL; - _object_t* obj = 0; - _var_t* var = 0; - _ht_node_t* ht = 0; - - mb_assert(data && h); - - ht = (_ht_node_t*)h; - obj = (_object_t*)data; - if(_is_internal_object(obj)) - goto _exit; - switch(obj->type) { - case _DT_VAR: - var = (_var_t*)obj->data.variable; - _gc_add_reachable(var->data, extra, ht); - - break; -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - if(!_ht_find(ht, &obj->data.usertype_ref->ref)) - _ht_set_or_insert(ht, &obj->data.usertype_ref->ref, obj->data.usertype_ref); - - break; -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_ARRAY_REF - case _DT_ARRAY: - if(!_ht_find(ht, &obj->data.array->ref)) - _ht_set_or_insert(ht, &obj->data.array->ref, obj->data.array); - - break; -#endif /* MB_ENABLE_ARRAY_REF */ -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: - if(!_ht_find(ht, &obj->data.list->ref)) { - _ht_set_or_insert(ht, &obj->data.list->ref, obj->data.list); - _LS_FOREACH(obj->data.list->list, _do_nothing_on_object, _gc_add_reachable, ht); - } - - break; - case _DT_DICT: - if(!_ht_find(ht, &obj->data.dict->ref)) { - _ht_set_or_insert(ht, &obj->data.dict->ref, obj->data.dict); - _HT_FOREACH(obj->data.dict->dict, _do_nothing_on_object, _gc_add_reachable_both, ht); - } - - break; - case _DT_LIST_IT: - if(!_ht_find(ht, &obj->data.list_it->list->ref)) { - _ht_set_or_insert(ht, &obj->data.list_it->list->ref, obj->data.list_it->list); - _LS_FOREACH(obj->data.list_it->list->list, _do_nothing_on_object, _gc_add_reachable, ht); - } - - break; - case _DT_DICT_IT: - if(!_ht_find(ht, &obj->data.dict_it->dict->ref)) { - _ht_set_or_insert(ht, &obj->data.dict_it->dict->ref, obj->data.dict_it->dict); - _HT_FOREACH(obj->data.dict_it->dict->dict, _do_nothing_on_object, _gc_add_reachable, ht); - } - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - if(!_ht_find(ht, &obj->data.instance->ref)) { - _ht_set_or_insert(ht, &obj->data.instance->ref, obj->data.instance); - _traverse_class(obj->data.instance, _gc_add_reachable, _add_class_meta_reachable, _META_LIST_MAX_DEPTH, false, ht, 0); - } - - break; -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_LAMBDA - case _DT_ROUTINE: - if(obj->data.routine->type == _IT_LAMBDA) { - if(!_ht_find(ht, &obj->data.routine->func.lambda.ref)) - _ht_set_or_insert(ht, &obj->data.routine->func.lambda.ref, obj->data.routine); - } - - break; -#endif /* MB_ENABLE_LAMBDA */ - default: /* Do nothing */ - break; - } - -_exit: - return result; -} - -/* Get reachable objects from both key and value */ -static int _gc_add_reachable_both(void* data, void* extra, void* h) { - int result = _OP_RESULT_NORMAL; - - mb_assert(data && extra && h); - - _gc_add_reachable(extra, 0, h); - _gc_add_reachable(data, extra, h); - - return result; -} - -/* Get all reachable referenced objects */ -static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht) { - _running_context_t* running = 0; - _ht_node_t* global_scope = 0; - - mb_assert(s && ht); - - running = s->running_context; - while(running) { - global_scope = running->var_dict; - if(global_scope) { - _HT_FOREACH(global_scope, _do_nothing_on_object, _gc_add_reachable, ht); - } - - running = running->prev; - } -} - -/* Destroy only the capsule (wrapper) of an object, leave the data behind, and add it to GC if possible */ -static int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - _ADDGC(obj, gc); - safe_free(obj); - - return result; -} - -/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ -static int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - - mb_assert(data); - - obj = (_object_t*)data; - _ADDGC(obj, gc); - safe_free(obj); - - obj = (_object_t*)extra; - _ADDGC(obj, gc); - safe_free(obj); - - return result; -} - -#ifdef MB_ENABLE_CLASS -/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ -static int _gc_destroy_garbage_in_class(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - if(obj->type == _DT_VAR) { - _gc_destroy_garbage_in_class(obj->data.variable->data, 0, gc); - safe_free(obj->data.variable->name); - safe_free(obj->data.variable); - } else { - _ADDGC(obj, gc); - } - safe_free(obj); - - return result; -} -#endif /* MB_ENABLE_CLASS */ - -#ifdef MB_ENABLE_LAMBDA -/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ -static int _gc_destroy_garbage_in_lambda(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - if(obj->type == _DT_VAR) { -#ifdef MB_ENABLE_CLASS - if(_is_string(obj) && obj->data.variable->pathing) { - safe_free(obj->data.variable->data); - } else { - _gc_destroy_garbage_in_lambda(obj->data.variable->data, 0, gc); - } -#else /* MB_ENABLE_CLASS */ - _gc_destroy_garbage_in_lambda(obj->data.variable->data, 0, gc); -#endif /* MB_ENABLE_CLASS */ - safe_free(obj->data.variable->name); - safe_free(obj->data.variable); - } else { - _ADDGC(obj, gc); - } - safe_free(obj); - - return result; -} - -/* Collect garbage of outer scopes */ -static void _gc_destroy_garbage_in_outer_scope(_running_context_ref_t* p, _gc_t* gc) { - while(p) { - _running_context_ref_t* scope = p; - p = p->prev; - _HT_FOREACH(scope->scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_lambda, gc); - _ht_clear(scope->scope->var_dict); - } -} -#endif /* MB_ENABLE_LAMBDA */ - -/* Destroy a garbage */ -static int _gc_destroy_garbage(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_NORMAL; - _ref_t* ref = 0; - bool_t proc = true; -#ifdef MB_ENABLE_COLLECTION_LIB - _list_t* lst = 0; - _dict_t* dct = 0; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_LAMBDA - _routine_t* routine = 0; -#endif /* MB_ENABLE_LAMBDA */ - - mb_assert(data && extra); - - ref = (_ref_t*)extra; - if(_ht_find(gc->collected_table, ref)) { - proc = true; - - goto _exit; - } - switch(ref->type) { -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: - lst = (_list_t*)data; - _LS_FOREACH(lst->list, _do_nothing_on_object, _gc_destroy_garbage_in_list, gc); - _ls_clear(lst->list); - lst->count = 0; - - break; - case _DT_DICT: - dct = (_dict_t*)data; - _HT_FOREACH(dct->dict, _do_nothing_on_object, _gc_destroy_garbage_in_dict, gc); - _ht_clear(dct->dict); - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_LAMBDA - case _DT_ROUTINE: - routine = (_routine_t*)data; - if(routine->type == _IT_LAMBDA) { - _HT_FOREACH(routine->func.lambda.scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_lambda, gc); - _ht_clear(routine->func.lambda.scope->var_dict); - if(routine->func.lambda.outer_scope && !_ht_find(gc->collected_table, &routine->func.lambda.outer_scope->ref)) - _gc_destroy_garbage_in_outer_scope(routine->func.lambda.outer_scope, gc); - } - - break; -#endif /* MB_ENABLE_LAMBDA */ -#ifdef MB_ENABLE_LAMBDA - case _DT_OUTER_SCOPE: /* Fall through */ -#endif /* MB_ENABLE_LAMBDA */ -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: /* Fall through */ -#endif /* MB_ENABLE_USERTYPE_REF */ - case _DT_ARRAY: /* Do nothing */ - break; - default: - proc = false; - - break; - } - if(proc && ref->count) - _unref(ref, data); - -_exit: - if(proc) - result = _OP_RESULT_DEL_NODE; - - return result; -} - -#ifdef MB_ENABLE_CLASS -/* Destroy a class instance garbage */ -static int _gc_destroy_garbage_class(void* data, void* extra, _gc_t* gc) { - int result = _OP_RESULT_NORMAL; - _ref_t* ref = 0; - bool_t proc = true; - _class_t* instance = 0; - - mb_assert(data && extra); - - ref = (_ref_t*)extra; - if(_ht_find(gc->collected_table, ref)) { - proc = true; - - goto _exit; - } - switch(ref->type) { - case _DT_CLASS: - instance = (_class_t*)data; - _HT_FOREACH(instance->scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_class, gc); - _ht_clear(instance->scope->var_dict); - _ls_clear(instance->meta_list); -#ifdef MB_ENABLE_LAMBDA - if(instance->scope->refered_lambdas) { - _ls_destroy(instance->scope->refered_lambdas); - instance->scope->refered_lambdas = 0; - } -#endif /* MB_ENABLE_LAMBDA */ - - break; - default: - proc = false; - - break; - } - if(proc && ref->count) - _unref(ref, data); - -_exit: - if(proc) - result = _OP_RESULT_DEL_NODE; - - return result; -} -#endif /* MB_ENABLE_CLASS */ - -/* Swap active garbage table and recursive table */ -static void _gc_swap_tables(mb_interpreter_t* s) { - _ht_node_t* tmp = 0; - - mb_assert(s); - - tmp = s->gc.table; - s->gc.table = s->gc.recursive_table; - s->gc.recursive_table = tmp; -} - -/* Try trigger garbage collection */ -static void _gc_try_trigger(mb_interpreter_t* s) { - mb_assert(s); - - if(_ht_count(s->gc.table) >= MB_GC_GARBAGE_THRESHOLD) - _gc_collect_garbage(s, 1); -} - -/* Collect all garbage */ -static void _gc_collect_garbage(mb_interpreter_t* s, int depth) { - _ht_node_t* valid = 0; - _gc_t* gc = 0; - - mb_assert(s); - - gc = &s->gc; - - /* Avoid infinity loop */ - if(gc->collecting) - return; - gc->collecting++; - - /* Get reachable information */ - valid = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); - if(depth != -1) - gc->valid_table = valid; - _gc_get_reachable(s, valid); - - /* Get unreachable information */ - _HT_FOREACH(valid, _do_nothing_on_object, _ht_remove_exist, gc->table); - - /* Collect garbage */ - do { -#ifdef MB_ENABLE_CLASS - _HT_FOREACH(gc->table, _do_nothing_on_object, _gc_destroy_garbage_class, &s->gc); -#endif /* MB_ENABLE_CLASS */ - _HT_FOREACH(gc->table, _do_nothing_on_object, _gc_destroy_garbage, &s->gc); - _ht_clear(gc->table); - if(gc->collecting > 1) - gc->collecting--; - - if(!depth || !_ht_count(gc->recursive_table)) - break; - - _gc_swap_tables(s); - gc->collecting++; - } while(1); - - /* Tidy */ - _ht_clear(gc->collected_table); - gc->valid_table = 0; - _ht_clear(valid); - _ht_destroy(valid); - gc->collecting--; - mb_assert(!gc->collecting); -} - -#ifdef MB_ENABLE_USERTYPE_REF -/* Create a referenced usertype */ -static _usertype_ref_t* _create_usertype_ref(mb_interpreter_t* s, void* val, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft) { - _usertype_ref_t* result = 0; - - mb_assert(s); - - result = (_usertype_ref_t*)mb_malloc(sizeof(_usertype_ref_t)); - memset(result, 0, sizeof(_usertype_ref_t)); - result->usertype = val; - result->dtor = un; - result->clone = cl; - result->hash = hs; - result->cmp = cp; - result->fmt = ft; - _create_ref(&result->ref, _unref_usertype_ref, _DT_USERTYPE_REF, s); - - return result; -} - -/* Destroy a referenced usertype */ -static void _destroy_usertype_ref(_usertype_ref_t* c) { - mb_assert(c); - - if(c->dtor) - c->dtor(c->ref.s, c->usertype); - if(c->calc_operators) { - safe_free(c->calc_operators); - } - _destroy_ref(&c->ref); - safe_free(c); -} - -/* Unreference a referenced usertype */ -static void _unref_usertype_ref(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_usertype_ref((_usertype_ref_t*)data); -} -#endif /* MB_ENABLE_USERTYPE_REF */ - -/* Create an array */ -static _array_t* _create_array(mb_interpreter_t* s, const char* n, _data_e t) { - _array_t* result = 0; - - mb_assert(s); - - result = (_array_t*)mb_malloc(sizeof(_array_t)); - memset(result, 0, sizeof(_array_t)); - result->type = t; - result->name = (char*)n; -#ifdef MB_ENABLE_ARRAY_REF - _create_ref(&result->ref, _unref_array, _DT_ARRAY, s); - _ref(&result->ref, result); -#else /* MB_ENABLE_ARRAY_REF */ - mb_unrefvar(s); -#endif /* MB_ENABLE_ARRAY_REF */ - - return result; -} - -#ifdef MB_ENABLE_ARRAY_REF -/* Destroy an array */ -static void _destroy_array(_array_t* arr) { - mb_assert(arr); - - _clear_array(arr); - if(arr->name) { - safe_free(arr->name); - } -#ifndef MB_SIMPLE_ARRAY - if(arr->types) { - safe_free(arr->types); - } -#endif /* MB_SIMPLE_ARRAY */ -#ifdef MB_ENABLE_ARRAY_REF - _destroy_ref(&arr->ref); -#endif /* MB_ENABLE_ARRAY_REF */ - safe_free(arr); -} -#endif /* MB_ENABLE_ARRAY_REF */ - -/* Initialize an array */ -static void _init_array(_array_t* arr) { - int elemsize = 0; - - mb_assert(arr); - -#ifdef MB_SIMPLE_ARRAY - elemsize = (int)_get_size_of(arr->type); -#else /* MB_SIMPLE_ARRAY */ - elemsize = (int)_get_size_of(_DT_UNKNOWN); -#endif /* MB_SIMPLE_ARRAY */ - mb_assert(arr->count > 0); - mb_assert(!arr->raw); - arr->raw = (void*)mb_malloc(elemsize * arr->count); - if(arr->raw) - memset(arr->raw, 0, elemsize * arr->count); -#ifndef MB_SIMPLE_ARRAY - arr->types = (_data_e*)mb_malloc(sizeof(_data_e) * arr->count); - if(arr->types) { - unsigned ul = 0; - for(ul = 0; ul < arr->count; ++ul) - arr->types[ul] = _DT_INT; - } -#endif /* MB_SIMPLE_ARRAY */ -} - -/* Clone an array */ -static _array_t* _clone_array(mb_interpreter_t* s, _array_t* arr) { - _array_t* result = 0; - unsigned index = 0; - mb_value_u val; - _data_e type = _DT_NIL; - - mb_assert(s && arr); - - result = _create_array(s, mb_strdup(arr->name, 0), arr->type); - result->count = arr->count; - result->dimension_count = arr->dimension_count; - memcpy(result->dimensions, arr->dimensions, sizeof(result->dimensions)); - _init_array(result); - for(index = 0; index < arr->count; index++) { - _get_array_elem(s, arr, index, &val, &type); - _set_array_elem(s, 0, result, index, &val, &type); - } - - return result; -} - -/* Calculate the true index of an array */ -static int _get_array_pos(mb_interpreter_t* s, _array_t* arr, int* d, int c) { - int result = 0; - int i = 0; - unsigned n = 0; - int f = 1; - - mb_assert(s && arr && d); - - if(c < 0 || c > arr->dimension_count) { - result = -1; - - goto _exit; - } - for(i = 0; i < c; i++) { - n = d[i]; - if(n >= arr->dimensions[i]) { - result = -1; - - goto _exit; - } - result += n * f; - f *= arr->dimensions[i]; - } - -_exit: - return result; -} - -/* Calculate the true index of an array, used when walking through an AST */ -static int _get_array_index(mb_interpreter_t* s, _ls_node_t** l, _object_t* c, unsigned* index, bool_t* literally) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* arr = 0; - _object_t* len = 0; - _object_t subscript; - _object_t* subscript_ptr = 0; - mb_value_u val; - int dcount = 0; - int f = 1; - unsigned idx = 0; - - mb_assert(s && l && index); - - subscript_ptr = &subscript; - _MAKE_NIL(subscript_ptr); - - if(literally) *literally = false; - - /* Array name */ - ast = *l; - if(!c && ast && _is_array(ast->data)) - c = (_object_t*)ast->data; - if(!_is_array(c)) { - _handle_error_on_obj(s, SE_RN_ARRAY_IDENTIFIER_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(((_object_t*)c)->type == _DT_ARRAY) - arr = (_object_t*)c; - else - arr = ((_object_t*)c)->data.variable->data; - /* = */ - if(literally && ast->next && _IS_FUNC((_object_t*)ast->next->data, _core_equal)) { - *literally = true; - - goto _exit; - } - /* ( */ - if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_FUNC || ((_object_t*)ast->next->data)->data.func->pointer != _core_open_bracket) { - _handle_error_on_obj( - s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, - (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, - MB_FUNC_ERR, _exit, result - ); - } - ast = ast->next; - /* Array subscript */ - if(!ast->next) { - _handle_error_on_obj(s, SE_RN_ARRAY_SUBSCRIPT_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - while(((_object_t*)ast->data)->type != _DT_FUNC || ((_object_t*)ast->data)->data.func->pointer != _core_close_bracket) { - /* Calculate an integer value */ - result = _calc_expression(s, &ast, &subscript_ptr); - if(result != MB_FUNC_OK) - goto _exit; - len = subscript_ptr; - if(!_try_get_value(len, &val, _DT_INT)) { - _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(val.integer < 0) { - _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(dcount + 1 > arr->data.array->dimension_count) { - _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if((unsigned)val.integer >= arr->data.array->dimensions[dcount]) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - idx += (unsigned)val.integer * f; - /* Comma? */ - if(_IS_SEP(ast->data, ',')) - ast = ast->next; - - f *= arr->data.array->dimensions[dcount]; - ++dcount; - } - *index = idx; - if(!arr->data.array->raw) { - _handle_error_on_obj(s, SE_RN_RANK_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - -_exit: - *l = ast; - - return result; -} - -/* Get the value of an element in an array */ -static bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type) { - bool_t result = true; - int_t elemsize = 0; - unsigned pos = 0; - void* rawptr = 0; - - mb_assert(s && arr && val && type); - mb_assert(index < arr->count); - - elemsize = _get_size_of(arr->type); - pos = (unsigned)(elemsize * index); - rawptr = (void*)((intptr_t)arr->raw + pos); - if(arr->type == _DT_REAL) { -#ifdef MB_SIMPLE_ARRAY - val->float_point = *((real_t*)rawptr); - *type = _DT_REAL; -#else /* MB_SIMPLE_ARRAY */ - _COPY_BYTES(val->bytes, *((mb_val_bytes_t*)rawptr)); - *type = arr->types[index]; -#endif /* MB_SIMPLE_ARRAY */ - } else if(arr->type == _DT_STRING) { - val->string = *((char**)rawptr); - *type = _DT_STRING; - } else { - mb_assert(0 && "Unsupported."); - } - - return result; -} - -/* Set the value of an element in an array */ -static int _set_array_elem(mb_interpreter_t* s, _ls_node_t* ast, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type) { - int result = MB_FUNC_OK; - int_t elemsize = 0; - unsigned pos = 0; - void* rawptr = 0; - mb_unrefvar(ast); - - mb_assert(s && arr && val); - mb_assert(index < arr->count); - - elemsize = _get_size_of(arr->type); - pos = (unsigned)(elemsize * index); - rawptr = (void*)((intptr_t)arr->raw + pos); -#ifdef MB_SIMPLE_ARRAY - switch(*type) { - case _DT_INT: - *((real_t*)rawptr) = (real_t)val->integer; - - break; - case _DT_STRING: { - size_t _sl = 0; - if(arr->type != _DT_STRING) { - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - _sl = strlen(val->string); - *((char**)rawptr) = (char*)mb_malloc(_sl + 1); - memcpy(*((char**)rawptr), val->string, _sl + 1); - } - - break; - default: - _COPY_BYTES(*((mb_val_bytes_t*)rawptr), val->bytes); - - break; - } -#else /* MB_SIMPLE_ARRAY */ - switch(*type) { - case _DT_STRING: { - size_t _sl = 0; - _sl = strlen(val->string); - *((char**)rawptr) = (char*)mb_malloc(_sl + 1); - memcpy(*((char**)rawptr), val->string, _sl + 1); - arr->types[index] = _DT_STRING; - } - - break; - default: - _COPY_BYTES(*((mb_val_bytes_t*)rawptr), val->bytes); - arr->types[index] = *type; - - break; - } -#endif /* MB_SIMPLE_ARRAY */ - - goto _exit; /* Avoid an unreferenced label warning */ - -_exit: - return result; -} - -/* Clear an array */ -static void _clear_array(_array_t* arr) { - char* str = 0; - int_t elemsize = 0; - unsigned pos = 0; - void* rawptr = 0; - unsigned ul = 0; - - mb_assert(arr); - - if(arr->raw) { -#ifndef MB_SIMPLE_ARRAY - if(arr->type == _DT_REAL) { - for(ul = 0; ul < arr->count; ++ul) { - if(arr->types[ul] == _DT_STRING) { - elemsize = _get_size_of(arr->type); - pos = (unsigned)(elemsize * ul); - rawptr = (void*)((intptr_t)arr->raw + pos); - str = *((char**)rawptr); - if(str) { - safe_free(str); - } - } - } - } -#endif /* MB_SIMPLE_ARRAY */ - if(arr->type == _DT_STRING) { - for(ul = 0; ul < arr->count; ++ul) { - elemsize = _get_size_of(arr->type); - pos = (unsigned)(elemsize * ul); - rawptr = (void*)((intptr_t)arr->raw + pos); - str = *((char**)rawptr); - if(str) { - safe_free(str); - } - } - } - safe_free(arr->raw); - } -} - -/* Determine if an object is an array value or an array variable */ -static bool_t _is_array(void* obj) { - bool_t result = false; - _object_t* o = 0; - - o = (_object_t*)obj; - if(o && o->type == _DT_ARRAY) - result = true; - else if(o && o->type == _DT_VAR) - result = o->data.variable->data->type == _DT_ARRAY; - - return result; -} - -#ifdef MB_ENABLE_ARRAY_REF -/* Unreference an array */ -static void _unref_array(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_array((_array_t*)data); -} -#endif /* MB_ENABLE_ARRAY_REF */ - -#ifdef MB_ENABLE_COLLECTION_LIB -/* Create a list */ -static _list_t* _create_list(mb_interpreter_t* s) { - _list_t* result = 0; - - mb_assert(s); - - result = (_list_t*)mb_malloc(sizeof(_list_t)); - memset(result, 0, sizeof(_list_t)); - result->list = _ls_create(); - _create_ref(&result->ref, _unref_list, _DT_LIST, s); - - return result; -} - -/* Destroy a list */ -static void _destroy_list(_list_t* c) { - mb_assert(c); - - if(c->range_begin) { safe_free(c->range_begin); } - _ls_foreach(c->list, _destroy_object); - _ls_destroy(c->list); - _destroy_ref(&c->ref); - safe_free(c); -} - -/* Create a dictionary */ -static _dict_t* _create_dict(mb_interpreter_t* s) { - _dict_t* result = 0; - - mb_assert(s); - - result = (_dict_t*)mb_malloc(sizeof(_dict_t)); - memset(result, 0, sizeof(_dict_t)); - result->dict = _ht_create(0, _ht_cmp_object, _ht_hash_object, _destroy_object_with_extra); - _create_ref(&result->ref, _unref_dict, _DT_DICT, s); - - return result; -} - -/* Destroy a dictionary */ -static void _destroy_dict(_dict_t* c) { - mb_assert(c); - - _ht_foreach(c->dict, _destroy_object_with_extra); - _ht_destroy(c->dict); - _destroy_ref(&c->ref); - safe_free(c); -} - -/* Create an iterator of a list */ -static _list_it_t* _create_list_it(_list_t* coll, bool_t lock) { - _list_it_t* result = 0; - - mb_assert(coll); - - result = (_list_it_t*)mb_malloc(sizeof(_list_it_t)); - memset(result, 0, sizeof(_list_it_t)); - result->list = coll; - result->locking = lock; - if(coll->range_begin) - result->curr.ranging = *coll->range_begin - sgn(coll->count); - else - result->curr.node = coll->list; - if(lock) - _lock_ref_object(&coll->lock, &coll->ref, coll); - _weak_ref(&coll->ref, coll, &result->weak_ref); - - return result; -} - -/* Destroy an iterator of a list */ -static bool_t _destroy_list_it(_list_it_t* it) { - bool_t result = true; - - mb_assert(it); - - if(_weak_unref(&it->weak_ref)) - _unlock_ref_object(&it->list->lock, &it->list->ref, it->list); - safe_free(it); - - return result; -} - -/* Move an iterator of a list to next step */ -static _list_it_t* _move_list_it_next(_list_it_t* it) { - _list_it_t* result = 0; - - if(!it || !it->list || !it->list->list) - goto _exit; - - if(it->list->lock < 0) { /* The iterator goes invalid if collection has been changed after obtaining iterator */ - result = it; - - goto _exit; - } - - if(!it->curr.node && !it->list->range_begin) - goto _exit; - - if(it->list->range_begin) { - if(it->list->lock) - it->curr.ranging += sgn(it->list->count); - - if(it->list->count > 0 && it->curr.ranging < *it->list->range_begin + it->list->count) - result = it; - else if(it->list->count < 0 && it->curr.ranging > *it->list->range_begin + it->list->count) - result = it; - } else { - if(it->list->lock) - it->curr.node = it->curr.node->next; - - if(it->curr.node) - result = it; - } - -_exit: - return result; -} - -/* Create an iterator of a dictionary */ -static _dict_it_t* _create_dict_it(_dict_t* coll, bool_t lock) { - _dict_it_t* result = 0; - - mb_assert(coll); - - result = (_dict_it_t*)mb_malloc(sizeof(_dict_it_t)); - memset(result, 0, sizeof(_dict_it_t)); - result->dict = coll; - result->locking = lock; - result->curr_bucket = 0; - result->curr_node = _INVALID_DICT_IT; - if(lock) - _lock_ref_object(&coll->lock, &coll->ref, coll); - _weak_ref(&coll->ref, coll, &result->weak_ref); - - return result; -} - -/* Destroy an iterator of a dictionary */ -static bool_t _destroy_dict_it(_dict_it_t* it) { - bool_t result = true; - - mb_assert(it); - - if(_weak_unref(&it->weak_ref)) - _unlock_ref_object(&it->dict->lock, &it->dict->ref, it->dict); - safe_free(it); - - return result; -} - -/* Move an iterator of a dictionary to next step */ -static _dict_it_t* _move_dict_it_next(_dict_it_t* it) { - _dict_it_t* result = 0; - - if(!it || !it->dict || !it->dict->dict || !it->curr_node) - goto _exit; - - if(!it->dict->lock) - goto _exit; - - if(it->curr_node && it->curr_node != _INVALID_DICT_IT) { - it->curr_node = it->curr_node->next; - if(!it->curr_node) - ++it->curr_bucket; - } - if(!it->curr_node || it->curr_node == _INVALID_DICT_IT) { - for( ; it->curr_bucket < it->dict->dict->array_size; ++it->curr_bucket) { - it->curr_node = it->dict->dict->array[it->curr_bucket]; - if(it->curr_node && it->curr_node->next) { - it->curr_node = it->curr_node->next; - - break; - } - } - } - - if(it->curr_node && it->curr_node->extra) - result = it; - -_exit: - return result; -} - -/* Unreference a list */ -static void _unref_list(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_list((_list_t*)data); -} - -/* Unreference a dictionary */ -static void _unref_dict(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_dict((_dict_t*)data); -} - -/* Push a value to a list */ -static void _push_list(_list_t* coll, mb_value_t* val, _object_t* oarg) { - mb_assert(coll && (val || oarg)); - - _fill_ranged(coll); - - if(val && !oarg) - _create_internal_object_from_public_value(val, &oarg); - _ls_pushback(coll->list, oarg); - coll->count++; - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); -} - -/* Pop a value from a list */ -static bool_t _pop_list(_list_t* coll, mb_value_t* val, mb_interpreter_t* s) { - _object_t* oval = 0; - - mb_assert(coll && val && s); - - _fill_ranged(coll); - - oval = (_object_t*)_ls_popback(coll->list); - if(oval) { - _internal_object_to_public_value(oval, val); - _destroy_object_capsule_only(oval, 0); - coll->count--; - - if(val->type == MB_DT_STRING) - _mark_lazy_destroy_string(s, val->value.string); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); - - return true; - } else { - mb_make_nil(*val); - - return false; - } -} - -/* Insert a value into a list */ -static bool_t _insert_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) { - _object_t* oarg = 0; - - mb_assert(coll && val); - - _fill_ranged(coll); - - _create_internal_object_from_public_value(val, &oarg); - if(oval) - *oval = oarg; - - if(_ls_insert_at(coll->list, (int)idx, oarg)) { - coll->count++; - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); - - return true; - } - - return false; -} - -/* Set an element in a list with a specific index with a given value */ -static bool_t _set_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) { - _ls_node_t* result = 0; - _object_t* oarg = 0; - - mb_assert(coll && (val || (oval && *oval))); - - _fill_ranged(coll); - - result = _node_at_list(coll, (int)idx); - if(result) { - if(result->data) - _destroy_object(result->data, 0); - if(val) { - _create_internal_object_from_public_value(val, &oarg); - if(oval) - *oval = oarg; - } else { - oarg = *oval; - } - result->data = oarg; - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); - } - - return !!(result && result->data); -} - -/* Remove an element in a list with a specific index */ -static bool_t _remove_at_list(_list_t* coll, int_t idx) { - bool_t result = false; - _ls_node_t* node = 0; - - mb_assert(coll); - - _fill_ranged(coll); - - node = _node_at_list(coll, (int)idx); - if(node) { - if(node->data) { - _ls_remove(coll->list, node, _destroy_object); - coll->count--; - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); - - result = true; - } - } - - return result; -} - -/* Get a node in a list with a specific index */ -static _ls_node_t* _node_at_list(_list_t* coll, int index) { - _ls_node_t* result = 0; - _ls_node_t* tmp = 0; - int n = 0; - - mb_assert(coll); - - _fill_ranged(coll); - - if(index >= 0 && index < (int)coll->count) { - /* Layout: HEAD ... LEFT ... PIVOT ... RIGHT ... TAIL - LEN(HEAD to LEFT) == LEN(LEFT to PIVOT) && LEN(PIVOT to RIGHT) == LEN(RIGHT to TAIL) - PIVOT is a cached node - */ - int head = 0, - left = coll->cached_index / 2, - right = coll->cached_index + (coll->count - coll->cached_index) / 2, - tail = coll->count - 1; - if(coll->cached_node) { - if(index >= head && index < left) { /* [HEAD, LEFT) */ - n = index; - tmp = coll->list->next; - while(tmp && n) { - tmp = tmp->next; - --n; - } - if(tmp) { - result = tmp; - coll->cached_node = tmp; - coll->cached_index = index; - } - } else if(index >= left && index <= right) { /* [LEFT, RIGHT] */ - while(index != coll->cached_index) { - if(index > coll->cached_index) { - coll->cached_node = coll->cached_node->next; - coll->cached_index++; - } else if(index < coll->cached_index) { - coll->cached_node = coll->cached_node->prev; - coll->cached_index--; - } - } - result = coll->cached_node; - } else if(index > right && index <= tail) { /* (RIGHT, TAIL] */ - n = tail - index; - tmp = coll->list->prev; - while(tmp && n) { - tmp = tmp->prev; - --n; - } - if(tmp) { - result = tmp; - coll->cached_node = tmp; - coll->cached_index = index; - } - } else { - mb_assert(0 && "Impossible."); - } - } else { - n = index; - tmp = coll->list->next; - while(tmp && n) { - tmp = tmp->next; - --n; - } - if(tmp) { - result = tmp; - coll->cached_node = tmp; - coll->cached_index = index; - } - } - } - - return result; -} - -/* Get the value in a list with a specific index */ -static bool_t _at_list(_list_t* coll, int_t idx, mb_value_t* oval) { - _ls_node_t* result = 0; - - mb_assert(coll && oval); - - _fill_ranged(coll); - - result = _node_at_list(coll, (int)idx); - if(oval && result && result->data) - _internal_object_to_public_value((_object_t*)result->data, oval); - - return !!(result && result->data); -} - -/* Find a value in a list */ -static bool_t _find_list(_list_t* coll, mb_value_t* val, int* idx) { - bool_t result = false; - _object_t* oarg = 0; - - mb_assert(coll && val); - - _fill_ranged(coll); - - _create_internal_object_from_public_value(val, &oarg); - result = !!_ls_find(coll->list, oarg, (_ls_compare)_ht_cmp_object, idx); - _destroy_object(oarg, 0); - - return result; -} - -/* Clear a list */ -static void _clear_list(_list_t* coll) { - mb_assert(coll); - - if(coll->range_begin) { safe_free(coll->range_begin); } - - _ls_foreach(coll->list, _destroy_object); - _ls_clear(coll->list); - coll->count = 0; - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); -} - -/* Sort a list */ -static void _sort_list(_list_t* coll) { - mb_assert(coll); - - _ls_sort(&coll->list, (_ls_compare)_ht_cmp_object); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); -} - -/* Invalidate cached list index */ -static void _invalidate_list_cache(_list_t* coll) { - mb_assert(coll); - - coll->cached_node = 0; - coll->cached_index = 0; -} - -/* Fill a ranged list with numbers */ -static void _fill_ranged(_list_t* coll) { - _object_t* obj = 0; - - mb_assert(coll); - - if(coll->range_begin) { - mb_value_t arg; - int_t begin = *coll->range_begin; - int_t end = *coll->range_begin + coll->count; - int_t step = sgn(coll->count); - - do { - mb_make_int(arg, begin); - _create_internal_object_from_public_value(&arg, &obj); - _ls_pushback(coll->list, obj); - - begin += step; - } while(begin != end); - safe_free(coll->range_begin); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - _invalidate_list_cache(coll); - } -} - -/* Set an element to a dictionary with a key-value pair */ -static void _set_dict(_dict_t* coll, mb_value_t* key, mb_value_t* val, _object_t* okey, _object_t* oval) { - _ls_node_t* exist = 0; - - mb_assert(coll && (key || okey) && (val || oval)); - - if(key && !okey) - _create_internal_object_from_public_value(key, &okey); - if(val && !oval) - _create_internal_object_from_public_value(val, &oval); - exist = _ht_find(coll->dict, okey); - if(exist) - _ht_remove(coll->dict, okey, _ls_cmp_extra_object); - _ht_set_or_insert(coll->dict, okey, oval); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); -} - -/* Remove an element to a dictionary with a specific key */ -static bool_t _remove_dict(_dict_t* coll, mb_value_t* key) { - _ls_node_t* result = 0; - _object_t* okey = 0; - void* data = 0; - void* extra = 0; - - mb_assert(coll && key); - - _create_internal_object_from_public_value(key, &okey); - result = _ht_find(coll->dict, okey); - if(result && result->data) { - data = result->data; - extra = result->extra; - _ht_remove(coll->dict, okey, _ls_cmp_extra_object); - _destroy_object(okey, 0); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); - } - - return !!(result && result->data); -} - -/* Find a key in a dictionary */ -static bool_t _find_dict(_dict_t* coll, mb_value_t* val, mb_value_t* oval) { - _ls_node_t* result = 0; - _object_t* oarg = 0; - - mb_assert(coll && val); - - _create_internal_object_from_public_value(val, &oarg); - result = _ht_find(coll->dict, oarg); - _destroy_object(oarg, 0); - if(oval) { - if(result && result->data) { - _internal_object_to_public_value((_object_t*)result->data, oval); - } else { - oval->type = MB_DT_UNKNOWN; - oval->value.integer = 0; - - return true; - } - } - - return !!(result && result->data); -} - -/* Clear a dictionary */ -static void _clear_dict(_dict_t* coll) { - mb_assert(coll); - - _ht_foreach(coll->dict, _destroy_object_with_extra); - _ht_clear(coll->dict); - - _write_on_ref_object(&coll->lock, &coll->ref, coll); -} - -/* Determin whether a list iterator is invalid */ -static bool_t _invalid_list_it(_list_it_t* it) { - if(!it) - return false; - - return it && it->list && it->list->lock <= 0; -} - -/* Determin whether a dictionary iterator is invalid */ -static bool_t _invalid_dict_it(_dict_it_t* it) { - if(!it) - return false; - - return it && it->dict && it->dict->lock <= 0; -} - -/* Assign an iterator to another object */ -static bool_t _assign_with_it(_object_t* tgt, _object_t* src) { - mb_assert(tgt && src); - - if(src->type != _DT_LIST_IT && src->type != _DT_DICT_IT) - return false; - - switch(src->type) { - case _DT_LIST_IT: - if(src->data.list_it->locking) { - tgt->data.list_it = _create_list_it(src->data.list_it->list, true); - } else { - tgt->data = src->data; - tgt->data.list_it->locking = true; - _lock_ref_object(&tgt->data.list_it->list->lock, &tgt->data.list_it->list->ref, tgt->data.list_it->list); - } - - break; - case _DT_DICT_IT: - if(src->data.dict_it->locking) { - tgt->data.dict_it = _create_dict_it(src->data.dict_it->dict, true); - } else { - tgt->data = src->data; - tgt->data.dict_it->locking = true; - _lock_ref_object(&tgt->data.dict_it->dict->lock, &tgt->data.dict_it->dict->ref, tgt->data.dict_it->dict); - } - - break; - default: - mb_assert(0 && "Impossible."); - - break; - } - - return true; -} - -/* Clone an object to a list */ -static int _clone_to_list(void* data, void* extra, _list_t* coll) { - _object_t* obj = 0; - _object_t* tgt = 0; - mb_unrefvar(extra); - - mb_assert(data && coll); - - _fill_ranged(coll); - - tgt = _create_object(); - obj = (_object_t*)data; - _clone_object(coll->ref.s, obj, tgt, false, false); - _push_list(coll, 0, tgt); - _REF(tgt) - - return 1; -} - -/* Clone a key-value pair to a dictionary */ -static int _clone_to_dict(void* data, void* extra, _dict_t* coll) { - _object_t* kobj = 0; - _object_t* ktgt = 0; - _object_t* vobj = 0; - _object_t* vtgt = 0; - - mb_assert(data && extra && coll); - - ktgt = _create_object(); - kobj = (_object_t*)extra; - _clone_object(coll->ref.s, kobj, ktgt, false, false); - - vtgt = _create_object(); - vobj = (_object_t*)data; - _clone_object(coll->ref.s, vobj, vtgt, false, false); - - _set_dict(coll, 0, 0, ktgt, vtgt); - _REF(ktgt) - _REF(vtgt) - - return 1; -} - -/* Copy an object from a list to an array */ -static int _copy_list_to_array(void* data, void* extra, _array_helper_t* h) { - _object_t* obj = 0; - mb_value_t val; - _data_e type = _DT_NIL; - mb_unrefvar(extra); - - mb_assert(data && h); - - obj = (_object_t*)data; - mb_make_nil(val); - _internal_object_to_public_value(obj, &val); - type = obj->type; - _set_array_elem(h->s, 0, h->array, h->index++, &val.value, &type); - - return 1; -} -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifdef MB_ENABLE_CLASS -/* Initialize a class */ -static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n) { - _running_context_t* running = 0; - _object_t* meobj = 0; - _var_t* me = 0; - - mb_assert(s && instance && n); - - running = s->running_context; - - memset(instance, 0, sizeof(_class_t)); - _create_ref(&instance->ref, _unref_class, _DT_CLASS, s); - _ref(&instance->ref, instance); - instance->name = n; - instance->meta_list = _ls_create(); - instance->scope = _create_running_context(true); - instance->created_from = instance; - - me = _create_var(&meobj, _CLASS_ME, strlen(_CLASS_ME) + 1, true); - me->data->type = _DT_CLASS; - me->data->data.instance = instance; - me->pathing = true; - me->isme = true; - _ht_set_or_insert(instance->scope->var_dict, me->name, meobj); -} - -/* Begin parsing a class */ -static void _begin_class(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - context->class_state = _CLASS_STATE_PROC; -} - -/* End parsing a class */ -static bool_t _end_class(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - if(context->routine_state) { - _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); - } - if(context->class_state == _CLASS_STATE_NONE) { - _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); - - return false; - } - context->class_state = _CLASS_STATE_NONE; - s->last_instance = 0; - - return true; -} - -/* Unreference a class instance */ -static void _unref_class(_ref_t* ref, void* data) { - mb_assert(ref); - - if(ref->s->valid) - _out_of_scope(ref->s, ((_class_t*)data)->scope, (_class_t*)data, 0, false); - - if(*ref->count == _NONE_REF) - _destroy_class((_class_t*)data); -} - -/* Destroy a class instance */ -static void _destroy_class(_class_t* c) { - mb_assert(c); - - if(c->meta_list) { - _unlink_meta_class(c->ref.s, c); - _ls_destroy(c->meta_list); - } - if(c->scope->var_dict) { - _ht_foreach(c->scope->var_dict, _destroy_object); - _ht_destroy(c->scope->var_dict); - } -#ifdef MB_ENABLE_LAMBDA - if(c->scope->refered_lambdas) { - _ls_destroy(c->scope->refered_lambdas); - c->scope->refered_lambdas = 0; - } -#endif /* MB_ENABLE_LAMBDA */ - safe_free(c->scope); - _destroy_ref(&c->ref); - safe_free(c->name); - safe_free(c); -} - -/* Traverse all fields of a class instance, and its meta class instances recursively as well */ -static bool_t _traverse_class(_class_t* c, _class_scope_walker scope_walker, _class_meta_walker meta_walker, unsigned meta_depth, bool_t meta_walk_on_self, void* extra_data, void* ret) { - bool_t result = true; - _ls_node_t* node = 0; - _class_t* meta = 0; - - mb_assert(c); - - if(scope_walker) { - _HT_FOREACH(c->scope->var_dict, _do_nothing_on_object, scope_walker, extra_data); - } - if(meta_walk_on_self) { - if(meta_walker) { - result = meta_walker(c, extra_data, ret); - if(!result) - goto _exit; - } - } - node = c->meta_list ? c->meta_list->next : 0; - while(node) { - meta = (_class_t*)node->data; - if(meta_walker && meta_depth) { - result = meta_walker(meta, extra_data, ret); - if(!result) - break; - } - result = _traverse_class( - meta, - scope_walker, - meta_walker, meta_depth ? meta_depth - 1 : 0, - meta_walk_on_self, - extra_data, ret - ); - if(!result) - break; - node = node->next; - } - -_exit: - return result; -} - -/* Link a class instance to the meta list of another class instance */ -static bool_t _link_meta_class(mb_interpreter_t* s, _class_t* derived, _class_t* base) { - mb_assert(s && derived && base); - - if(_ls_find(derived->meta_list, base, (_ls_compare)_ht_cmp_intptr, 0)) { - _handle_error_now(s, SE_RN_WRONG_META_CLASS, s->source_file, MB_FUNC_ERR); - - return false; - } - - _ls_pushback(derived->meta_list, base); - _ref(&base->ref, base); - - return true; -} - -/* Unlink all meta class instances of a class instance */ -static void _unlink_meta_class(mb_interpreter_t* s, _class_t* derived) { - mb_assert(s && derived); - - _LS_FOREACH(derived->meta_list, _do_nothing_on_object, _unlink_meta_instance, derived); - _ls_clear(derived->meta_list); -} - -/* Unlink a meta class instance */ -static int _unlink_meta_instance(void* data, void* extra, _class_t* derived) { - _class_t* base = 0; - mb_unrefvar(extra); - - mb_assert(data && derived); - - base = (_class_t*)data; - _unref(&base->ref, base); - - return 0; -} - -/* Clone fields of a class instance to another */ -static int _clone_clsss_field(void* data, void* extra, void* n) { - int result = _OP_RESULT_NORMAL; - _object_t* obj = 0; - _array_t* arr = 0; - _var_t* var = 0; - _routine_t* sub = 0; - _class_t* instance = (_class_t*)n; - _object_t* ret = 0; - mb_unrefvar(extra); - - mb_assert(data && n); - - obj = (_object_t*)data; - if(_is_internal_object(obj)) - goto _exit; - switch(obj->type) { - case _DT_VAR: - var = obj->data.variable; - if(!_IS_ME(var)) { - if(_ht_find(instance->scope->var_dict, var->name)) - break; - - ret = _duplicate_parameter(var, 0, instance->scope); - _clone_object(instance->ref.s, obj, ret->data.variable->data, false, var->data->type != _DT_CLASS); - } - - break; - case _DT_ARRAY: - arr = obj->data.array; - if(!_ht_find(instance->scope->var_dict, arr->name)) { - ret = _create_object(); - ret->type = _DT_ARRAY; - ret->ref = false; - _clone_object(instance->ref.s, obj, ret, false, false); - - _ht_set_or_insert(instance->scope->var_dict, ret->data.array->name, ret); - } - - break; - case _DT_ROUTINE: - sub = obj->data.routine; - if(!_ht_find(instance->scope->var_dict, sub->name)) { - _routine_t* routine = _clone_routine(sub, instance, false); - ret = _create_object(); - ret->type = _DT_ROUTINE; - ret->data.routine = routine; - ret->ref = false; - - _ht_set_or_insert(instance->scope->var_dict, obj->data.routine->name, ret); - } - - break; - default: /* Do nothing */ - break; - } - -_exit: - return result; -} - -/* Link meta class to a new instance */ -static bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret) { - _class_t* instance = (_class_t*)n; - mb_unrefvar(ret); - - mb_assert(meta && n); - - _link_meta_class(instance->ref.s, instance, meta); - - return true; -} - -/* Search for a meta function with a specific name and assign to a member field */ -static int _search_class_meta_function(mb_interpreter_t* s, _class_t* instance, const char* n, _routine_t* _mb_unaligned * f) { - _ls_node_t* node = 0; - - mb_assert(s); - - node = _search_identifier_in_class(s, instance, n, 0, 0); - if(f) *f = 0; - if(node) { - _object_t* obj = (_object_t*)node->data; - if(obj && _IS_ROUTINE(obj)) { - if(f) *f = obj->data.routine; - - return 1; - } - } - - return 0; -} - -/* Search for the HASH and COMPARE meta function for a class */ -static int _search_class_hash_and_compare_functions(mb_interpreter_t* s, _class_t* instance) { - mb_assert(s && instance); - - _search_class_meta_function(s, instance, _CLASS_HASH_FUNC, &instance->hash); - _search_class_meta_function(s, instance, _CLASS_COMPARE_FUNC, &instance->compare); - - if(!instance->hash && !instance->compare) { - return MB_FUNC_OK; - } else if(instance->hash && instance->compare) { - return MB_FUNC_OK; - } else { - instance->hash = 0; - instance->compare = 0; - - return MB_FUNC_WARNING; - } -} - -/* Detect whether a class instance is inherited from another */ -static bool_t _is_a_class(_class_t* instance, void* m, void* ret) { - _class_t* meta = (_class_t*)m; - bool_t* r = (bool_t*)ret; - bool_t is_a = false; - - mb_assert(instance && meta && ret); - - do { - if(instance == meta) { - is_a = true; - - break; - } - if(instance == instance->created_from) - break; - instance = instance->created_from; - } while(1); - - *r = is_a; - - return !(*r); -} - -/* Add a meta class instance to a GC reachable table */ -static bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret) { - _ht_node_t* htable = (_ht_node_t*)ht; - mb_unrefvar(ret); - - mb_assert(meta && ht); - - if(!_ht_find(htable, &meta->ref)) - _ht_set_or_insert(htable, &meta->ref, meta); - - return true; -} - -#ifdef MB_ENABLE_COLLECTION_LIB -/* Reflect each field of a class instance to a dictionary */ -static int _reflect_class_field(void* data, void* extra, void* d) { - int result = _OP_RESULT_NORMAL; - _object_t* obj = 0; - _var_t* var = 0; - _routine_t* sub = 0; - _dict_t* coll = (_dict_t*)d; - mb_unrefvar(extra); - - mb_assert(data && d); - - obj = (_object_t*)data; - if(_is_internal_object(obj)) - goto _exit; - switch(obj->type) { - case _DT_VAR: - var = (_var_t*)obj->data.variable; - if(!_ht_find(coll->dict, var->name)) { - mb_value_t kv, vv; - mb_make_string(kv, var->name); - _internal_object_to_public_value(obj, &vv); - _set_dict(coll, &kv, &vv, 0, 0); - } - - break; - case _DT_ROUTINE: - sub = (_routine_t*)obj->data.routine; - if(!_ht_find(coll->dict, sub->name)) { - mb_value_t kv, vv; - mb_make_string(kv, sub->name); - mb_make_type(vv, _internal_type_to_public_type(obj->type)); - _set_dict(coll, &kv, &vv, 0, 0); - } - - break; - default: /* Do nothing */ - break; - } - -_exit: - return result; -} -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* Call the TOSTRING function of a class instance to format it */ -static int _format_class_to_string(mb_interpreter_t* s, void** l, _class_t* instance, _object_t* out, bool_t* got_tostr) { - int result = MB_FUNC_OK; - - mb_assert(s && l && instance && out); - - _ls_node_t* tsn = _search_identifier_in_class(s, instance, _CLASS_TOSTRING_FUNC, 0, 0); - if(got_tostr) *got_tostr = false; - if(tsn) { - _object_t* tso = (_object_t*)tsn->data; - _ls_node_t* tmp = (_ls_node_t*)*l; - mb_value_t va[1]; - mb_make_nil(va[0]); - if(_eval_routine(s, &tmp, va, 1, tso->data.routine, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { - _MAKE_NIL(out); - _public_value_to_internal_object(&s->running_context->intermediate_value, out); - if(out->type == _DT_STRING) { - out->data.string = mb_strdup(out->data.string, strlen(out->data.string) + 1); - out->ref = false; - mb_make_nil(s->running_context->intermediate_value); - } else { - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - if(got_tostr) - *got_tostr = true; - } - } - -_exit: - return result; -} - -/* Reflect a class instance from a string */ -static _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg) { - _ls_node_t* cs = 0; - _object_t* c = 0; - - cs = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); - if(!cs) - return 0; - c = (_object_t*)cs->data; - if(!c) - return 0; - c = _GET_CLASS(c); - if(!c) - return 0; - if(arg) - _internal_object_to_public_value(c, arg); - - return c->data.instance; -} - -/* Detect whether it's accessing a member of a class instance following a sub routine */ -static bool_t _is_valid_class_accessor_following_routine(mb_interpreter_t* s, _var_t* var, _ls_node_t* ast, _ls_node_t** out) { - bool_t result = false; - _running_context_t* running = 0; - - mb_assert(s && var && ast); - - running = s->running_context; - - if(out) *out = 0; - - if(_is_accessor_char(*var->name) && (ast && ast->prev && _IS_FUNC(ast->prev->data, _core_close_bracket)) && running->intermediate_value.type == MB_DT_CLASS) { - _class_t* instance = (_class_t*)running->intermediate_value.value.instance; - _ls_node_t* fn = _search_identifier_in_class(s, instance, var->name + 1, 0, 0); - result = true; - if(fn && out) - *out = fn; - } - - return result; -} -#endif /* MB_ENABLE_CLASS */ - -/* Initialize a routine */ -static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f) { - _running_context_t* running = 0; - - mb_assert(s && routine); - - running = s->running_context; - - memset(routine, 0, sizeof(_routine_t)); - routine->name = n; - - if(n && f) - routine->type = _IT_NATIVE; - else if(n && !f) - routine->type = _IT_SCRIPT; -#ifdef MB_ENABLE_LAMBDA - else if(!n && !f) - routine->type = _IT_LAMBDA; -#endif /* MB_ENABLE_LAMBDA */ - - switch(routine->type) { - case _IT_SCRIPT: - routine->func.basic.scope = _create_running_context(true); - - break; -#ifdef MB_ENABLE_LAMBDA - case _IT_LAMBDA: - _create_ref(&routine->func.lambda.ref, _unref_routine, _DT_ROUTINE, s); - _ref(&routine->func.lambda.ref, routine); - - break; -#endif /* MB_ENABLE_LAMBDA */ - case _IT_NATIVE: - routine->func.native.entry = f; - - break; - } - -#ifdef MB_ENABLE_SOURCE_TRACE - if(s->source_file) - routine->source_file = mb_strdup(s->source_file, 0); -#endif /* MB_ENABLE_SOURCE_TRACE */ -} - -/* Begin parsing a routine */ -static int _begin_routine(mb_interpreter_t* s) { - int result = MB_FUNC_OK; - _parsing_context_t* context = 0; - unsigned short before = 0; - - mb_assert(s); - - context = s->parsing_context; - before = context->routine_state++; - if(before > context->routine_state) { - context->routine_state--; - result = MB_FUNC_ERR; - _handle_error_now(s, SE_RN_TOO_MANY_ROUTINES, s->last_error_file, result); - } - - return result; -} - -/* End parsing a routine */ -static bool_t _end_routine(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - if(!context->routine_state) { - _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); - - return false; - } - context->routine_state--; - - return true; -} - -/* Begin parsing the parameter list of a routine */ -static void _begin_routine_parameter_list(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - context->routine_params_state++; -} - -/* End parsing the parameter list of a routine */ -static void _end_routine_parameter_list(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - context->routine_params_state--; -} - -/* Duplicate a parameter from a parameter list to variable dictionary */ -static _object_t* _duplicate_parameter(void* data, void* extra, _running_context_t* running) { - _var_t* ref = 0; - _var_t* var = 0; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(running); - - if(data == 0) - return 0; - - ref = (_var_t*)data; - - var = _create_var(&obj, ref->name, 0, true); - - _ht_set_or_insert(running->var_dict, var->name, obj); - - return obj; -} - -/* Clone a routine */ -static _routine_t* _clone_routine(_routine_t* sub, void* c, bool_t toupval) { - _routine_t* result = 0; -#ifdef MB_ENABLE_CLASS - _class_t* instance = (_class_t*)c; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(c); -#endif /* MB_ENABLE_CLASS */ - - mb_assert(sub); - -#ifdef MB_ENABLE_LAMBDA - if(toupval || sub->type == _IT_LAMBDA) - result = sub; -#else /* MB_ENABLE_LAMBDA */ - mb_unrefvar(toupval); -#endif /* MB_ENABLE_LAMBDA */ - - if(!result) { - result = (_routine_t*)mb_malloc(sizeof(_routine_t)); - memset(result, 0, sizeof(_routine_t)); - result->name = sub->name; -#ifdef MB_ENABLE_SOURCE_TRACE - result->source_file = sub->source_file; -#endif /* MB_ENABLE_SOURCE_TRACE */ -#ifdef MB_ENABLE_CLASS - result->instance = instance; -#endif /* MB_ENABLE_CLASS */ - result->is_cloned = true; - result->type = sub->type; - result->func = sub->func; - } - - return result; -} - -#ifdef MB_ENABLE_LAMBDA -/* Initialize a lambda */ -static _running_context_t* _init_lambda(mb_interpreter_t* s, _routine_t* routine) { - _running_context_t* result = 0; - _lambda_t* lambda = 0; - - mb_assert(s && routine); - - _init_routine(s, routine, 0, 0); - mb_assert(routine->type == _IT_LAMBDA); - lambda = &routine->func.lambda; - lambda->scope = _create_running_context(true); - result = _push_scope_by_routine(s, lambda->scope); - - return result; -} - -/* Unreference a lambda routine */ -static void _unref_routine(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_routine(ref->s, (_routine_t*)data); -} - -/* Destroy a lambda routine */ -static void _destroy_routine(mb_interpreter_t* s, _routine_t* r) { - _gc_t* gc = 0; - - mb_assert(r); - - if(s) gc = &s->gc; - if(!r->is_cloned) { - if(r->name) { - safe_free(r->name); - } -#ifdef MB_ENABLE_SOURCE_TRACE - if(r->source_file) { - safe_free(r->source_file); - } -#endif /* MB_ENABLE_SOURCE_TRACE */ - switch(r->type) { - case _IT_SCRIPT: - if(r->func.basic.scope) { - _destroy_scope(s, r->func.basic.scope); - r->func.basic.scope = 0; - } - if(r->func.basic.parameters) - _ls_destroy(r->func.basic.parameters); - - break; - case _IT_LAMBDA: - _destroy_ref(&r->func.lambda.ref); - if(r->func.lambda.scope->var_dict) { - _ht_foreach(r->func.lambda.scope->var_dict, _destroy_object); - _ht_destroy(r->func.lambda.scope->var_dict); - } - safe_free(r->func.lambda.scope); - if(r->func.lambda.parameters) - _ls_destroy(r->func.lambda.parameters); - if(r->func.lambda.outer_scope && !_ht_find(gc->collected_table, &r->func.lambda.outer_scope->ref)) - _unref(&r->func.lambda.outer_scope->ref, r->func.lambda.outer_scope); - if(r->func.lambda.upvalues) - _ht_destroy(r->func.lambda.upvalues); - - break; - case _IT_NATIVE: /* Do nothing */ - break; - } - } - safe_free(r); -} - -/* Mark an upvalue of a lambda */ -static void _mark_upvalue(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj, const char* n) { - _running_context_t* running = 0; - _running_context_t* found_in_scope = 0; - _ls_node_t* scp = 0; - - mb_assert(s && lambda && obj); - - running = s->running_context; - scp = _search_identifier_in_scope_chain(s, running, n, _PATHING_NORMAL, 0, &found_in_scope); - if(scp && found_in_scope) { - if(!found_in_scope->refered_lambdas) - found_in_scope->refered_lambdas = _ls_create(); - if(!_ls_find(found_in_scope->refered_lambdas, lambda, (_ls_compare)_ht_cmp_intptr, 0)) - _ls_pushback(found_in_scope->refered_lambdas, lambda); - } - - if(!lambda->upvalues) - lambda->upvalues = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); - _ht_set_or_insert(lambda->upvalues, obj->data.variable->name, obj); -} - -/* Try to mark upvalues of a lambda */ -static void _try_mark_upvalue(mb_interpreter_t* s, _routine_t* r, _object_t* obj) { - _lambda_t* lambda = 0; - _ls_node_t* node = 0; - _object_t* inner = 0; - - mb_assert(s && r && obj); - mb_assert(r->type == _IT_LAMBDA); - - lambda = &r->func.lambda; - - switch(obj->type) { - case _DT_VAR: - node = _ht_find(lambda->scope->var_dict, obj->data.variable->name); - if(node && node->data) { - /* Use variables in the inner scope */ - inner = (_object_t*)node->data; - obj->type = _DT_VAR; - obj->data = inner->data; - obj->ref = true; - } else { - /* Mark upvalues referencing outer scope chain */ - _mark_upvalue(s, lambda, obj, obj->data.variable->name); - } - - break; - default: /* Do nothing */ - break; - } -} - -/* Create an outer scope, which is a referenced type */ -static _running_context_ref_t* _create_outer_scope(mb_interpreter_t* s) { - _running_context_ref_t* result = 0; - - mb_assert(s); - - result = (_running_context_ref_t*)mb_malloc(sizeof(_running_context_ref_t)); - memset(result, 0, sizeof(_running_context_ref_t)); - _create_ref(&result->ref, _unref_outer_scope, _DT_OUTER_SCOPE, s); - result->scope = _create_running_context(true); - - return result; -} - -/* Unreference an outer scope */ -static void _unref_outer_scope(_ref_t* ref, void* data) { - mb_assert(ref); - - if(*ref->count == _NONE_REF) - _destroy_outer_scope((_running_context_ref_t*)data); -} - -/* Destroy an outer scope */ -static void _destroy_outer_scope(_running_context_ref_t* p) { - mb_assert(p); - - if(p) { - _running_context_ref_t* scope = p; - p = p->prev; - _destroy_scope(scope->ref.s, scope->scope); - _destroy_ref(&scope->ref); - safe_free(scope); - } - while(p) { - _running_context_ref_t* scope = p; - p = p->prev; - _unref(&scope->ref, scope); - } -} - -/* Do nothing, this is a helper function for lambda */ -static int _do_nothing_on_ht_for_lambda(void* data, void* extra) { - int result = _OP_RESULT_NORMAL; - mb_unrefvar(data); - mb_unrefvar(extra); - - return result; -} - -/* Fill an outer scope with the original value */ -static int _fill_with_upvalue(void* data, void* extra, _upvalue_scope_tuple_t* tuple) { - _object_t* obj = (_object_t*)data; - const char* n = (const char*)extra; - unsigned ul = 0; - _ls_node_t* ast = 0; - _ls_node_t* nput = 0; - - nput = _ht_find(tuple->outer_scope->scope->var_dict, (void*)n); - if(!nput) { - _ls_node_t* nori = 0; -#ifdef MB_ENABLE_CLASS - if(tuple->instance) - nori = _search_identifier_in_scope_chain(tuple->s, tuple->scope, n, _PATHING_NORMAL, 0, 0); - else - nori = _ht_find(tuple->scope->var_dict, (void*)n); -#else /* MB_ENABLE_CLASS */ - nori = _ht_find(tuple->scope->var_dict, (void*)n); -#endif /* MB_ENABLE_CLASS */ - if(nori) { - _object_t* ovar = 0; - _var_t* var = _create_var(&ovar, n, 0, true); - obj = (_object_t*)nori->data; - _clone_object(tuple->s, obj, var->data, true, true); - _REF(var->data) - if(_IS_ROUTINE(obj) && obj->data.routine->type != _IT_LAMBDA) { - ovar->ref = true; - var->data->ref = true; - } -#ifdef MB_ENABLE_CLASS - if(obj->type == _DT_VAR) - var->pathing = obj->data.variable->pathing; - else - var->pathing = false; -#endif /* MB_ENABLE_CLASS */ - ul = _ht_set_or_insert(tuple->outer_scope->scope->var_dict, ovar->data.variable->name, ovar); - mb_assert(ul); - _ht_set_or_insert(tuple->filled, extra, data); - - ast = tuple->lambda->entry; - while(ast && ast != tuple->lambda->end->next) { - _object_t* aobj = (_object_t*)ast->data; - switch(aobj->type) { - case _DT_VAR: - if(!strcmp(aobj->data.variable->name, ovar->data.variable->name)) { - aobj->type = _DT_VAR; - aobj->data = ovar->data; - aobj->ref = true; - } - - break; - default: /* Do nothing */ - break; - } - ast = ast->next; - } - } - } - - return 0; -} - -/* Remove filled upvalues */ -static int _remove_filled_upvalue(void* data, void* extra, _ht_node_t* ht) { - _ht_remove_exist(data, extra, ht); - - return _OP_RESULT_NORMAL; -} - -/* Fill an outer scope with the original one */ -static int _fill_outer_scope(void* data, void* extra, _upvalue_scope_tuple_t* tuple) { - _lambda_t* lambda = (_lambda_t*)data; - mb_unrefvar(extra); - - if(lambda->upvalues) { - tuple->filled = _ht_create(0, _ht_cmp_intptr, _ht_hash_intptr, 0); { - tuple->lambda = lambda; - _HT_FOREACH(lambda->upvalues, _do_nothing_on_ht_for_lambda, _fill_with_upvalue, tuple); - tuple->lambda = 0; - } - _HT_FOREACH(tuple->filled, _do_nothing_on_ht_for_lambda, _remove_filled_upvalue, lambda->upvalues); - if(!_ht_count(lambda->upvalues)) { - _ht_destroy(lambda->upvalues); - lambda->upvalues = 0; - } - _ht_destroy(tuple->filled); - } - - if(lambda->outer_scope) { - _running_context_ref_t* root_ref = _get_root_ref_scope(lambda->outer_scope); - root_ref->prev = tuple->outer_scope; - root_ref->scope->prev = tuple->outer_scope->scope; - } else { - lambda->outer_scope = tuple->outer_scope; - } - - _ref(&tuple->outer_scope->ref, tuple->outer_scope); - - return 0; -} - -/* Link the local scope of a lambda and all upvalue scopes in chain to a given scope */ -static _running_context_t* _link_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) { - _running_context_ref_t* root_ref = 0; - _running_context_t* root = 0; - - if(lambda->outer_scope) { - lambda->scope->prev = lambda->outer_scope->scope; - root_ref = _get_root_ref_scope(lambda->outer_scope); - root_ref->scope->prev = 0; - } - root = _get_root_scope(lambda->scope); - - if(weak) { - _push_weak_scope_by_routine(s, root, 0); - } else { - root->prev = s->running_context; - s->running_context = lambda->scope; - } - - return lambda->scope; -} - -/* Unlink the local scope of a lambda and all upvalue scopes in chain from a given scope */ -static _running_context_t* _unlink_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) { - _running_context_ref_t* root_ref = 0; - _running_context_t* root = 0; - - if(lambda->outer_scope) { - root_ref = _get_root_ref_scope(lambda->outer_scope); - root = root_ref->scope; - } else { - root = lambda->scope; - } - - if(weak) - _pop_weak_scope(s, root); - else - s->running_context = root->prev; - - root->prev = 0; - lambda->scope->prev = 0; - - return lambda->scope; -} - -/* Check whether an object is a valid lambda body node */ -static bool_t _is_valid_lambda_body_node(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj) { - mb_unrefvar(s); - mb_unrefvar(lambda); - - return ( - !_IS_FUNC(obj, _core_def) && - !_IS_FUNC(obj, _core_enddef) && -#ifdef MB_ENABLE_CLASS - !_IS_FUNC(obj, _core_class) && - !_IS_FUNC(obj, _core_endclass) && -#endif /* MB_ENABLE_CLASS */ - true - ); -} -#endif /* MB_ENABLE_LAMBDA */ - -#ifdef MB_ENABLE_CLASS -/* Create a scope reference to an exist one by a class */ -static _running_context_t* _reference_scope_by_class(mb_interpreter_t* s, _running_context_t* p, _class_t* c) { - _running_context_t* result = 0; - mb_unrefvar(c); - - mb_assert(s && p); - - if(p->meta == _SCOPE_META_REF) - p = p->ref; - - result = _create_running_context(false); - result->meta = _SCOPE_META_REF; - result->ref = p; - - return result; -} - -/* Push a scope by a class */ -static _running_context_t* _push_scope_by_class(mb_interpreter_t* s, _running_context_t* p) { - mb_assert(s); - - if(_find_scope(s, p)) - p = _reference_scope_by_class(s, p, 0); - p->prev = s->running_context; - s->running_context = p; - - return s->running_context; -} - -/* Try to search an identifire from a class */ -static _ls_node_t* _search_identifier_in_class(mb_interpreter_t* s, _class_t* instance, const char* n, _ht_node_t** ht, _running_context_t** sp) { - _ls_node_t* result = 0; - _ls_node_t* node = 0; - _class_t* meta = 0; - - mb_assert(s && instance && n); - - result = _ht_find(instance->scope->var_dict, (void*)n); - if(result) { - if(ht) *ht = instance->scope->var_dict; - if(sp) *sp = instance->scope; - } - - if(!result) { - node = instance->meta_list ? instance->meta_list->next : 0; - while(node) { - meta = (_class_t*)node->data; - result = _search_identifier_in_class(s, meta, n, ht, sp); - if(result) - break; - node = node->next; - } - } - - return result; -} -#endif /* MB_ENABLE_CLASS */ - -/* Create a scope reference to an exist one by a routine */ -static _running_context_t* _reference_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) { - _running_context_t* result = 0; - - mb_assert(s && p); - - if(p->meta == _SCOPE_META_REF) - p = p->ref; - - result = _create_running_context(false); - result->meta = _SCOPE_META_REF; - result->ref = p; - if(r && r->func.basic.parameters) { - result->var_dict = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); - _LS_FOREACH(r->func.basic.parameters, _do_nothing_on_object, _duplicate_parameter, result); - } - - return result; -} - -/* Push a weak scope by a routine */ -static _running_context_t* _push_weak_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) { - mb_assert(s); - - if(_find_scope(s, p)) - p = _reference_scope_by_routine(s, p, r); - if(p) - p->prev = s->running_context; - - return p; -} - -/* Push a scope by a routine */ -static _running_context_t* _push_scope_by_routine(mb_interpreter_t* s, _running_context_t* p) { - mb_assert(s); - - if(_find_scope(s, p)) - p = _reference_scope_by_routine(s, p, 0); - if(p) { - p->prev = s->running_context; - s->running_context = p; - } - - return s->running_context; -} - -/* Destroy a scope */ -static void _destroy_scope(mb_interpreter_t* s, _running_context_t* p) { - mb_unrefvar(s); - - if(p->var_dict) { - _ht_foreach(p->var_dict, _destroy_object); - _ht_destroy(p->var_dict); -#ifdef MB_ENABLE_LAMBDA - if(p->refered_lambdas) { - _ls_destroy(p->refered_lambdas); - p->refered_lambdas = 0; - } -#endif /* MB_ENABLE_LAMBDA */ - } - safe_free(p); -} - -/* Pop a weak scope */ -static _running_context_t* _pop_weak_scope(mb_interpreter_t* s, _running_context_t* p) { - mb_assert(s); - - if(p) - p->prev = 0; - - return p; -} - -/* Pop a scope */ -static _running_context_t* _pop_scope(mb_interpreter_t* s, bool_t tidy) { - _running_context_t* running = 0; - - mb_assert(s); - - running = s->running_context; - s->running_context = running->prev; - running->prev = 0; - if(running->meta == _SCOPE_META_REF) - _destroy_scope(s, running); - else if(tidy) - _out_of_scope(s, running, 0, 0, true); - - return s->running_context; -} - -/* Out of current scope */ -static void _out_of_scope(mb_interpreter_t* s, _running_context_t* running, void* instance, _routine_t* routine, bool_t lose) { -#ifdef MB_ENABLE_LAMBDA - _upvalue_scope_tuple_t tuple; -#endif /* MB_ENABLE_LAMBDA */ - mb_unrefvar(routine); - - mb_assert(s); - -#ifdef MB_ENABLE_LAMBDA - if(running->refered_lambdas) { - tuple.s = s; -# ifdef MB_ENABLE_CLASS - tuple.instance = (_class_t*)instance; -# else /* MB_ENABLE_CLASS */ - mb_unrefvar(instance); -# endif /* MB_ENABLE_CLASS */ - tuple.scope = running; - tuple.outer_scope = _create_outer_scope(s); - tuple.lambda = 0; - - _LS_FOREACH(running->refered_lambdas, _do_nothing_on_ht_for_lambda, _fill_outer_scope, &tuple); - - _ls_destroy(running->refered_lambdas); - running->refered_lambdas = 0; - } -#else /* MB_ENABLE_LAMBDA */ - mb_unrefvar(instance); -#endif /* MB_ENABLE_LAMBDA */ - - if(lose) { - if(running->var_dict) - _ht_foreach(running->var_dict, _lose_object); - } -} - -/* Find a scope in a scope chain */ -static _running_context_t* _find_scope(mb_interpreter_t* s, _running_context_t* p) { - _running_context_t* running = 0; - - mb_assert(s); - - running = s->running_context; - while(running) { - if(running == p) - return running; - - if(running->ref == p) - return running->ref; - - running = running->prev; - } - - return running; -} - -/* Get the root scope in a scope chain */ -static _running_context_t* _get_root_scope(_running_context_t* scope) { - _running_context_t* result = 0; - - while(scope) { - result = scope; - scope = scope->prev; - } - - return result; -} - -#ifdef MB_ENABLE_LAMBDA -/* Get the root referenced scope in a referenced scope chain */ -static _running_context_ref_t* _get_root_ref_scope(_running_context_ref_t* scope) { - _running_context_ref_t* result = 0; - - while(scope) { - result = scope; - scope = scope->prev; - } - - return result; -} -#endif /* MB_ENABLE_LAMBDA */ - -/* Get a proper scope to define a routine */ -static _running_context_t* _get_scope_to_add_routine(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - _running_context_t* running = 0; - unsigned short class_state = _CLASS_STATE_NONE; - - mb_assert(s); - - context = s->parsing_context; - running = s->running_context; -#ifdef MB_ENABLE_CLASS - class_state = context->class_state; -#endif /* MB_ENABLE_CLASS */ - if(class_state != _CLASS_STATE_NONE) { - if(running) - running = running->prev; - } else { - while(running) { - if(running->meta == _SCOPE_META_ROOT) - break; - - running = running->prev; - } - } - - return running; -} - -/* Try to search an identifier in a scope chain */ -static _ls_node_t* _search_identifier_in_scope_chain(mb_interpreter_t* s, _running_context_t* scope, const char* n, int pathing, _ht_node_t** ht, _running_context_t** sp) { - _ls_node_t* result = 0; - _running_context_t* running = 0; - _ht_node_t* fn = 0; - _running_context_t* fs = 0; - - mb_assert(s && n); - -#ifdef MB_ENABLE_CLASS - if(pathing) { - result = _search_identifier_accessor(s, scope, n, &fn, &fs, pathing == _PATHING_UNKNOWN_FOR_NOT_FOUND); - if(result) - goto _exit; - } - - if(s->last_routine && s->last_routine->instance) { - _class_t* lastinst = s->last_routine->instance; - s->last_routine->instance = 0; - result = _search_identifier_in_class(s, lastinst, n, &fn, &fs); - s->last_routine->instance = lastinst; - if(result) - goto _exit; - } -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(pathing); -#endif /* MB_ENABLE_CLASS */ - - if(scope) - running = scope; - else - running = s->running_context; - while(running && !result) { - if(running->var_dict) { - result = _ht_find(running->var_dict, (void*)n); - fn = running->var_dict; - fs = running; - if(!result && running->meta == _SCOPE_META_REF) { - result = _ht_find(running->ref->var_dict, (void*)n); - fn = running->ref->var_dict; - fs = running->ref; - } - if(result) - break; - } - - running = running->prev; - } - -#ifdef MB_ENABLE_CLASS -_exit: -#endif /* MB_ENABLE_CLASS */ - if(ht) *ht = fn; - if(sp) *sp = fs; - - return result; -} - -/* Try to search an array in a scope chain */ -static _array_t* _search_array_in_scope_chain(mb_interpreter_t* s, _array_t* i, _object_t** o) { - _object_t* obj = 0; - _ls_node_t* scp = 0; - _array_t* result = 0; - - mb_assert(s && i); - - result = i; - scp = _search_identifier_in_scope_chain(s, 0, result->name, _PATHING_NONE, 0, 0); - if(scp) { - obj = (_object_t*)scp->data; - if(obj && obj->type == _DT_ARRAY) { - result = obj->data.array; - if(o) *o = obj; - } - } - - return result; -} - -/* Try to search a variable in a scope chain */ -static _var_t* _search_var_in_scope_chain(mb_interpreter_t* s, _var_t* i) { - _object_t* obj = 0; - _ls_node_t* scp = 0; - _var_t* result = 0; - - mb_assert(s && i); - - result = i; - scp = _search_identifier_in_scope_chain(s, 0, result->name, _PATHING_NORMAL, 0, 0); - if(scp) { - obj = (_object_t*)scp->data; - if(obj && obj->type == _DT_VAR) - result = obj->data.variable; - } - - return result; -} - -/* Try to search an identifier accessor in a scope */ -static _ls_node_t* _search_identifier_accessor(mb_interpreter_t* s, _running_context_t* scope, const char* n, _ht_node_t** ht, _running_context_t** sp, bool_t unknown_for_not_found) { - _ls_node_t* result = 0; - _object_t* obj = 0; - char acc[_SINGLE_SYMBOL_MAX_LENGTH]; - int i = 0; - int j = 0; -#ifdef MB_ENABLE_CLASS - _class_t* instance = 0; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(unknown_for_not_found); -#endif /* MB_ENABLE_CLASS */ - - mb_assert(s && n); - - while((i == 0) || (i > 0 && n[i - 1])) { - acc[j] = n[i]; - if(_is_accessor_char(acc[j]) || acc[j] == _ZERO_CHAR) { - acc[j] = _ZERO_CHAR; - do { -#ifdef MB_ENABLE_CLASS - if(instance) { - result = _search_identifier_in_class(s, instance, acc, ht, sp); - if(!result && unknown_for_not_found) { - result = (_ls_node_t*)&_LS_NODE_UNKNOWN; - - return result; - } - - break; - } -#endif /* MB_ENABLE_CLASS */ - - result = _search_identifier_in_scope_chain(s, scope, acc, _PATHING_NONE, ht, sp); - } while(0); - - if(!result) - return 0; - obj = (_object_t*)result->data; - if(!obj) - return 0; - switch(obj->type) { - case _DT_VAR: -#ifdef MB_ENABLE_CLASS - if(obj->data.variable->data->type == _DT_CLASS) - instance = obj->data.variable->data->data.instance; -#endif /* MB_ENABLE_CLASS */ - - break; -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - instance = obj->data.instance; - - break; -#endif /* MB_ENABLE_CLASS */ - case _DT_ARRAY: /* Fall through */ - case _DT_ROUTINE: /* Do nothing */ - break; - default: - mb_assert(0 && "Unsupported."); - - return 0; - } - - j = 0; - i++; - - continue; - } - j++; - i++; - } - - return result; -} - -/* Create a variable object */ -static _var_t* _create_var(_object_t** oobj, const char* n, size_t ns, bool_t dup_name) { - _object_t* obj = 0; - _var_t* var = 0; - - var = (_var_t*)mb_malloc(sizeof(_var_t)); - memset(var, 0, sizeof(_var_t)); - if(dup_name) - var->name = mb_strdup(n, ns); - else - var->name = (char*)n; - var->data = _create_object(); - - if(!oobj || !(*oobj)) - obj = _create_object(); - else - obj = *oobj; - _MAKE_NIL(obj); - obj->type = _DT_VAR; - obj->data.variable = var; - obj->ref = false; - - if(oobj) *oobj = obj; - - return var; -} - -/* Create an _object_t struct */ -static _object_t* _create_object(void) { - _object_t* result = 0; - - result = (_object_t*)mb_malloc(sizeof(_object_t)); - _MAKE_NIL(result); - - return result; -} - -/* Clone the data of an object */ -static int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt, bool_t toupval, bool_t deep) { - int result = 0; - - mb_assert(obj && tgt); - - _MAKE_NIL(tgt); - tgt->type = _DT_NIL; - if(_is_internal_object(obj)) - goto _exit; - tgt->type = obj->type; - switch(obj->type) { - case _DT_VAR: - _clone_object(s, obj->data.variable->data, tgt, toupval, deep); - - break; - case _DT_STRING: - tgt->data.string = mb_strdup(obj->data.string, 0); - - break; -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - tgt->data.usertype_ref = _create_usertype_ref( - obj->data.usertype_ref->ref.s, - obj->data.usertype_ref->clone(obj->data.usertype_ref->ref.s, obj->data.usertype_ref->usertype), - obj->data.usertype_ref->dtor, obj->data.usertype_ref->clone, - obj->data.usertype_ref->hash, obj->data.usertype_ref->cmp, obj->data.usertype_ref->fmt - ); - if(obj->data.usertype_ref->calc_operators) { - tgt->data.usertype_ref->calc_operators = (_calculation_operator_info_t*)mb_malloc(sizeof(_calculation_operator_info_t)); - memcpy(tgt->data.usertype_ref->calc_operators, obj->data.usertype_ref->calc_operators, sizeof(_calculation_operator_info_t)); - } - tgt->data.usertype_ref->coll_func = obj->data.usertype_ref->coll_func; - tgt->data.usertype_ref->generic_func = obj->data.usertype_ref->generic_func; - _ref(&tgt->data.usertype_ref->ref, tgt->data.usertype_ref); - - break; -#endif /* MB_ENABLE_USERTYPE_REF */ - case _DT_FUNC: - tgt->data.func->name = mb_strdup(obj->data.func->name, strlen(obj->data.func->name) + 1); - tgt->data.func->pointer = obj->data.func->pointer; - - break; - case _DT_ARRAY: - if(deep) { - tgt->data.array = _clone_array(s, obj->data.array); - } else { - tgt->data.array = obj->data.array; -#ifdef MB_ENABLE_ARRAY_REF - _ref(&obj->data.array->ref, obj->data.array); -#endif /* MB_ENABLE_ARRAY_REF */ - } - - break; -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: - if(deep) { - tgt->data.list = _create_list(obj->data.list->ref.s); - _ref(&tgt->data.list->ref, tgt->data.list); - _LS_FOREACH(obj->data.list->list, _do_nothing_on_object, _clone_to_list, tgt->data.list); - } else { - tgt->data.list = obj->data.list; - _ref(&obj->data.list->ref, obj->data.list); - } - - break; - case _DT_LIST_IT: - tgt->data.list_it = _create_list_it(obj->data.list_it->list, true); - - break; - case _DT_DICT: - if(deep) { - tgt->data.dict = _create_dict(obj->data.dict->ref.s); - _ref(&tgt->data.dict->ref, tgt->data.dict); - _HT_FOREACH(obj->data.dict->dict, _do_nothing_on_object, _clone_to_dict, tgt->data.dict); - } else { - tgt->data.dict = obj->data.dict; - _ref(&obj->data.dict->ref, obj->data.dict); - } - - break; - case _DT_DICT_IT: - tgt->data.dict_it = _create_dict_it(obj->data.dict_it->dict, true); - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ - case _DT_LABEL: - tgt->data.label->name = mb_strdup(obj->data.label->name, 0); - tgt->data.label->node = obj->data.label->node; - - break; -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - if(deep) { - tgt->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_class(s, tgt->data.instance, mb_strdup(obj->data.instance->name, 0)); - tgt->data.instance->created_from = obj->data.instance->created_from; - _push_scope_by_class(s, tgt->data.instance->scope); - _traverse_class(obj->data.instance, _clone_clsss_field, _clone_class_meta_link, _META_LIST_MAX_DEPTH, false, tgt->data.instance, 0); - if(_search_class_hash_and_compare_functions(s, tgt->data.instance) != MB_FUNC_OK) { - mb_assert(0 && "Impossible."); - } - tgt->data.instance->userdata = obj->data.instance->userdata; - _pop_scope(s, false); - } else { - tgt->data.instance = obj->data.instance; - _ref(&obj->data.instance->ref, obj->data.instance); - } - - break; -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - tgt->data.routine = _clone_routine( - obj->data.routine, -#ifdef MB_ENABLE_CLASS - obj->data.routine->instance, -#else /* MB_ENABLE_CLASS */ - 0, -#endif /* MB_ENABLE_CLASS */ - toupval - ); - - break; - case _DT_NIL: /* Fall through */ - case _DT_UNKNOWN: /* Fall through */ - case _DT_INT: /* Fall through */ - case _DT_REAL: /* Fall through */ - case _DT_TYPE: /* Fall through */ - case _DT_SEP: /* Fall through */ - case _DT_EOS: /* Fall through */ - case _DT_USERTYPE: - tgt->data = obj->data; - - break; - default: - mb_assert(0 && "Invalid type."); - - break; - } - tgt->ref = false; -#ifdef MB_ENABLE_SOURCE_TRACE - tgt->source_pos = 0; - tgt->source_row = 0; - tgt->source_col = 0; -#else /* MB_ENABLE_SOURCE_TRACE */ - tgt->source_pos = 0; -#endif /* MB_ENABLE_SOURCE_TRACE */ - ++result; - -_exit: - return result; -} - -/* Dispose the data of an object */ -static int _dispose_object(_object_t* obj) { - int result = 0; - _var_t* var = 0; - - mb_assert(obj); - - if(_is_internal_object(obj)) - goto _exit; - switch(obj->type) { - case _DT_VAR: - if(!obj->ref) { - var = (_var_t*)obj->data.variable; - safe_free(var->name); - mb_assert(var->data->type != _DT_VAR); - if(_IS_ME(var)) - _destroy_object_capsule_only(var->data, 0); - else - _destroy_object(var->data, 0); - safe_free(var); - } - - break; - case _DT_STRING: - if(!obj->ref) { - if(obj->data.string) { - safe_free(obj->data.string); - } - } - - break; - case _DT_FUNC: - safe_free(obj->data.func->name); - safe_free(obj->data.func); - - break; - _UNREF_USERTYPE_REF(obj) - _UNREF_ARRAY(obj) - _UNREF_COLL(obj) - _UNREF_CLASS(obj) - _UNREF_COLL_IT(obj) - _UNREF_ROUTINE(obj) - case _DT_LABEL: - if(!obj->ref) { - safe_free(obj->data.label->name); - safe_free(obj->data.label); - } - - break; -#ifdef MB_ENABLE_SOURCE_TRACE - case _DT_PREV_IMPORT: /* Fall through */ - case _DT_POST_IMPORT: - if(!obj->ref) { - if(obj->data.import_info) { - if(obj->data.import_info->source_file) { - safe_free(obj->data.import_info->source_file); - } - safe_free(obj->data.import_info); - } - } - - break; -#endif /* MB_ENABLE_SOURCE_TRACE */ - case _DT_NIL: /* Fall through */ - case _DT_UNKNOWN: /* Fall through */ - case _DT_INT: /* Fall through */ - case _DT_REAL: /* Fall through */ - case _DT_TYPE: /* Fall through */ - case _DT_SEP: /* Fall through */ - case _DT_EOS: /* Fall through */ - case _DT_USERTYPE: /* Do nothing */ - break; - default: - mb_assert(0 && "Invalid type."); - - break; - } - obj->ref = false; - obj->type = _DT_NIL; - memset(&obj->data, 0, sizeof(obj->data)); -#ifdef MB_ENABLE_SOURCE_TRACE - obj->source_pos = 0; - obj->source_row = 0; - obj->source_col = 0; -#else /* MB_ENABLE_SOURCE_TRACE */ - obj->source_pos = 0; -#endif /* MB_ENABLE_SOURCE_TRACE */ - ++result; - -_exit: - return result; -} - -/* Destroy an object and its data */ -static int _destroy_object(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - if(!_dispose_object(obj)) - goto _exit; - safe_free(obj); - -_exit: - return result; -} - -/* Destroy an object, including its data and extra data */ -static int _destroy_object_with_extra(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - - mb_assert(data); - - obj = (_object_t*)data; - if(!_dispose_object(obj)) - goto _exit; - safe_free(obj); - obj = (_object_t*)extra; - if(!_dispose_object(obj)) - goto _exit; - safe_free(obj); - -_exit: - return result; -} - -/* Destroy an object which is not come from compile time */ -static int _destroy_object_not_compile_time(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - if(!obj->source_pos) { - if(!_dispose_object(obj)) - goto _exit; - safe_free(obj); - } - -_exit: - return result; -} - -/* Destroy only the capsule (wrapper) of an object, leave the data behind */ -static int _destroy_object_capsule_only(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _object_t* obj = 0; - mb_unrefvar(extra); - - mb_assert(data); - - obj = (_object_t*)data; - safe_free(obj); - - return result; -} - -/* Do nothing with an object, this is a helper function */ -static int _do_nothing_on_object(void* data, void* extra) { - int result = _OP_RESULT_NORMAL; - mb_unrefvar(data); - mb_unrefvar(extra); - - return result; -} - -/* Lose an object of of scope */ -static int _lose_object(void* data, void* extra) { - int result = _OP_RESULT_NORMAL; - _object_t* obj = 0; - bool_t make_nil = true; - - mb_assert(data && extra); - - obj = (_object_t*)data; - switch(obj->type) { - case _DT_VAR: - _lose_object(obj->data.variable->data, extra); - make_nil = false; - - break; - _UNREF_USERTYPE_REF(obj) - _UNREF_ARRAY(obj) - _UNREF_COLL(obj) - _UNREF_CLASS(obj) - _UNREF_ROUTINE(obj) - default: - make_nil = false; - - break; - } - - if(make_nil) { - _MAKE_NIL(obj); - } - - return result; -} - -/* Remove an object referenced to source code */ -static int _remove_source_object(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - mb_unrefvar(extra); - - mb_assert(data); - - return result; -} - -/* Destroy a chunk of memory */ -static int _destroy_memory(void* data, void* extra) { - int result = _OP_RESULT_NORMAL; - mb_unrefvar(extra); - - mb_assert(data); - - safe_free(data); - - return result; -} - -/* Compare two numbers from two objects */ -static int _compare_numbers(const _object_t* first, const _object_t* second) { - int result = 0; - - mb_assert(first && second); - mb_assert((first->type == _DT_INT || first->type == _DT_REAL) && (second->type == _DT_INT || second->type == _DT_REAL)); - - if(first->type == _DT_INT && second->type == _DT_INT) { - if(first->data.integer > second->data.integer) - result = 1; - else if(first->data.integer < second->data.integer) - result = -1; - } else if(first->type == _DT_REAL && second->type == _DT_REAL) { - if(first->data.float_point > second->data.float_point) - result = 1; - else if(first->data.float_point < second->data.float_point) - result = -1; - } else { - if((first->type == _DT_INT ? (real_t)first->data.integer : first->data.float_point) > (second->type == _DT_INT ? (real_t)second->data.integer : second->data.float_point)) - result = 1; - else if((first->type == _DT_INT ? (real_t)first->data.integer : first->data.float_point) > (second->type == _DT_INT ? (real_t)second->data.integer : second->data.float_point)) - result = -1; - } - - return result; -} - -/* Determine whether an object is internal */ -static bool_t _is_internal_object(_object_t* obj) { - bool_t result = false; - - mb_assert(obj); - - result = ( - (_exp_assign == obj) || - (_OBJ_BOOL_TRUE == obj) || (_OBJ_BOOL_FALSE == obj) - ); - - return result; -} - -/* Convert a public mb_data_e type to an internal _data_e */ -static _data_e _public_type_to_internal_type(mb_data_e t) { - switch(t) { - case MB_DT_NIL: - return _DT_NIL; - case MB_DT_INT: - return _DT_INT; - case MB_DT_REAL: - return _DT_REAL; - case MB_DT_STRING: - return _DT_STRING; - case MB_DT_TYPE: - return _DT_TYPE; - case MB_DT_USERTYPE: - return _DT_USERTYPE; -#ifdef MB_ENABLE_USERTYPE_REF - case MB_DT_USERTYPE_REF: - return _DT_USERTYPE_REF; -#endif /* MB_ENABLE_USERTYPE_REF */ - case MB_DT_ARRAY: - return _DT_ARRAY; -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - return _DT_LIST; - case MB_DT_LIST_IT: - return _DT_LIST_IT; - case MB_DT_DICT: - return _DT_DICT; - case MB_DT_DICT_IT: - return _DT_DICT_IT; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: - return _DT_CLASS; -#endif /* MB_ENABLE_CLASS */ - case MB_DT_ROUTINE: - return _DT_ROUTINE; - default: - return _DT_UNKNOWN; - } -} - -/* Convert an internal mb_data_e type to a public _data_e */ -static mb_data_e _internal_type_to_public_type(_data_e t) { - switch(t) { - case _DT_NIL: - return MB_DT_NIL; - case _DT_INT: - return MB_DT_INT; - case _DT_REAL: - return MB_DT_REAL; - case _DT_STRING: - return MB_DT_STRING; - case _DT_TYPE: - return MB_DT_TYPE; - case _DT_USERTYPE: - return MB_DT_USERTYPE; -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - return MB_DT_USERTYPE_REF; -#endif /* MB_ENABLE_USERTYPE_REF */ - case _DT_ARRAY: - return MB_DT_ARRAY; -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: - return MB_DT_LIST; - case _DT_LIST_IT: - return MB_DT_LIST_IT; - case _DT_DICT: - return MB_DT_DICT; - case _DT_DICT_IT: - return MB_DT_DICT_IT; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - return MB_DT_CLASS; -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - return MB_DT_ROUTINE; - default: - return MB_DT_UNKNOWN; - } -} - -/* Assign a public mb_value_t to an internal _object_t */ -static int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn) { - int result = MB_FUNC_OK; - - mb_assert(pbl && itn); - - _UNREF(itn) - - switch(pbl->type) { - case MB_DT_NIL: - itn->type = _DT_NIL; - itn->data.integer = false; - - break; - case MB_DT_UNKNOWN: - itn->type = _DT_UNKNOWN; - itn->data.integer = false; - - break; - case MB_DT_INT: - itn->type = _DT_INT; - itn->data.integer = pbl->value.integer; - - break; - case MB_DT_REAL: - itn->type = _DT_REAL; - itn->data.float_point = pbl->value.float_point; - - break; - case MB_DT_STRING: - itn->type = _DT_STRING; - itn->data.string = pbl->value.string; - itn->ref = true; - - break; - case MB_DT_TYPE: - itn->type = _DT_TYPE; - itn->data.type = pbl->value.type; - - break; - case MB_DT_USERTYPE: - itn->type = _DT_USERTYPE; - memcpy(itn->data.raw, pbl->value.bytes, sizeof(mb_val_bytes_t)); - - break; -#ifdef MB_ENABLE_USERTYPE_REF - case MB_DT_USERTYPE_REF: - itn->type = _DT_USERTYPE_REF; - itn->data.usertype_ref = (_usertype_ref_t*)pbl->value.usertype_ref; - - break; -#endif /* MB_ENABLE_USERTYPE_REF */ - case MB_DT_ARRAY: - itn->type = _DT_ARRAY; - itn->data.array = (_array_t*)pbl->value.array; - itn->ref = false; - - break; -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - itn->type = _DT_LIST; - itn->data.list = (_list_t*)pbl->value.list; - - break; - case MB_DT_LIST_IT: - itn->type = _DT_LIST_IT; - itn->data.list_it = (_list_it_t*)pbl->value.list_it; - - break; - case MB_DT_DICT: - itn->type = _DT_DICT; - itn->data.dict = (_dict_t*)pbl->value.dict; - - break; - case MB_DT_DICT_IT: - itn->type = _DT_DICT_IT; - itn->data.dict_it = (_dict_it_t*)pbl->value.dict_it; - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: - itn->type = _DT_CLASS; - itn->data.instance = (_class_t*)pbl->value.instance; - - break; -#endif /* MB_ENABLE_CLASS */ - case MB_DT_ROUTINE: - itn->type = _DT_ROUTINE; - itn->data.routine = (_routine_t*)pbl->value.routine; - - break; - default: - result = MB_FUNC_ERR; - - break; - } - - return result; -} - -/* Assign an internal _object_t to a public mb_value_t */ -static int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl) { - int result = MB_FUNC_OK; - - mb_assert(pbl && itn); - - switch(itn->type) { - case _DT_VAR: - result = _internal_object_to_public_value(itn->data.variable->data, pbl); - - break; - case _DT_NIL: - mb_make_nil(*pbl); - - break; - case _DT_UNKNOWN: - pbl->type = MB_DT_UNKNOWN; - pbl->value.integer = false; - - break; - case _DT_INT: - pbl->type = MB_DT_INT; - pbl->value.integer = itn->data.integer; - - break; - case _DT_REAL: - pbl->type = MB_DT_REAL; - pbl->value.float_point = itn->data.float_point; - - break; - case _DT_STRING: - pbl->type = MB_DT_STRING; - pbl->value.string = itn->data.string; - - break; - case _DT_TYPE: - pbl->type = MB_DT_TYPE; - pbl->value.type = itn->data.type; - - break; - case _DT_USERTYPE: - pbl->type = MB_DT_USERTYPE; - memcpy(pbl->value.bytes, itn->data.raw, sizeof(mb_val_bytes_t)); - - break; -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: - pbl->type = MB_DT_USERTYPE_REF; - pbl->value.usertype_ref = itn->data.usertype_ref; - - break; -#endif /* MB_ENABLE_USERTYPE_REF */ - case _DT_ARRAY: - pbl->type = MB_DT_ARRAY; - pbl->value.array = itn->data.array; - - break; -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: - pbl->type = MB_DT_LIST; - pbl->value.list = itn->data.list; - - break; - case _DT_LIST_IT: - pbl->type = MB_DT_LIST_IT; - pbl->value.list_it = itn->data.list; - - break; - case _DT_DICT: - pbl->type = MB_DT_DICT; - pbl->value.dict = itn->data.dict; - - break; - case _DT_DICT_IT: - pbl->type = MB_DT_DICT_IT; - pbl->value.dict_it = itn->data.dict_it; - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - pbl->type = MB_DT_CLASS; - pbl->value.instance = itn->data.instance; - - break; -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - pbl->type = MB_DT_ROUTINE; - pbl->value.routine = itn->data.routine; - - break; - default: - result = MB_FUNC_ERR; - - break; - } - - return result; -} - -/* Create an internal object from a public value */ -static int _create_internal_object_from_public_value(mb_value_t* pbl, _object_t** itn) { - int result = MB_FUNC_OK; - - mb_assert(pbl && itn); - - *itn = _create_object(); - _public_value_to_internal_object(pbl, *itn); - if((*itn)->type == _DT_STRING) { - (*itn)->ref = false; - (*itn)->data.string = mb_strdup((*itn)->data.string, 0); - } - - return result; -} - -/* Compare a public value and an internal object */ -static int _compare_public_value_and_internal_object(mb_value_t* pbl, _object_t* itn) { - int result = 0; - mb_value_t tmp; - - mb_make_nil(tmp); - _internal_object_to_public_value(itn, &tmp); - if(pbl->type != tmp.type) - result = pbl->type - tmp.type; - else - result = mb_memcmp(pbl->value.bytes, tmp.value.bytes, sizeof(mb_val_bytes_t)); - - return result; -} - -/* Try clear the intermediate value */ -static void _try_clear_intermediate_value(void* data, void* extra, mb_interpreter_t* s) { - _object_t* obj = 0; - _running_context_t* running = 0; - mb_unrefvar(extra); - - mb_assert(s); - - if(data == 0) - return; - - obj = (_object_t*)data; - running = s->running_context; - - if(!_compare_public_value_and_internal_object(&running->intermediate_value, obj)) { - mb_make_nil(running->intermediate_value); - } -} - -/* Remove from another list if exist */ -static void _remove_if_exist(void* data, void* extra, _ls_node_t* ls) { - _object_t* obj = 0; - mb_unrefvar(extra); - - obj = (_object_t*)data; - _ls_try_remove(ls, obj, _ls_cmp_data, 0); -} - -/* Destroy an object in variable argument list */ -static void _destroy_var_arg(void* data, void* extra, _gc_t* gc) { - _object_t* obj = 0; - mb_unrefvar(extra); - mb_unrefvar(gc); - - mb_assert(data); - - obj = (_object_t*)data; - _UNREF(obj); - safe_free(obj); -} - -/* Destroy edge destroying objects */ -static void _destroy_edge_objects(mb_interpreter_t* s) { - mb_assert(s); - - _ls_foreach(s->edge_destroy_objects, _destroy_object); - _ls_clear(s->edge_destroy_objects); -} - -/* Mark a string as an edge destroying object */ -static void _mark_edge_destroy_string(mb_interpreter_t* s, char* ch) { - _object_t* temp_obj = 0; - - mb_assert(s && ch); - - temp_obj = _create_object(); - temp_obj->type = _DT_STRING; - temp_obj->ref = false; - temp_obj->data.string = ch; - _ls_pushback(s->edge_destroy_objects, temp_obj); -} - -/* Destroy lazy destroying objects */ -static void _destroy_lazy_objects(mb_interpreter_t* s) { - mb_assert(s); - - _ls_foreach(s->lazy_destroy_objects, _destroy_object); - _ls_clear(s->lazy_destroy_objects); -} - -/* Mark a string as a lazy destroying object */ -static void _mark_lazy_destroy_string(mb_interpreter_t* s, char* ch) { - _object_t* temp_obj = 0; - - mb_assert(s && ch); - - temp_obj = _create_object(); - temp_obj->type = _DT_STRING; - temp_obj->ref = false; - temp_obj->data.string = ch; - _ls_pushback(s->lazy_destroy_objects, temp_obj); -} - -/* Assign a value with another */ -static void _assign_public_value(mb_value_t* tgt, mb_value_t* src) { - _object_t obj; - mb_value_t nil; - - mb_assert(tgt); - - _MAKE_NIL(&obj); - _public_value_to_internal_object(tgt, &obj); - _UNREF(&obj) - - mb_make_nil(nil); - if(!src) - src = &nil; - memcpy(tgt, src, sizeof(mb_value_t)); - *src = nil; -} - -/* Swap two public values */ -static void _swap_public_value(mb_value_t* tgt, mb_value_t* src) { - mb_value_t tmp; - - mb_assert(tgt && src); - - tmp = *tgt; - *tgt = *src; - *src = tmp; -} - -/* Clear the scope chain */ -static int _clear_scope_chain(mb_interpreter_t* s) { - int result = 0; - _running_context_t* running = 0; - _running_context_t* prev = 0; - - mb_assert(s); - - running = s->running_context; - while(running) { - prev = running->prev; - - _ht_foreach(running->var_dict, _destroy_object); - _ht_clear(running->var_dict); -#ifdef MB_ENABLE_LAMBDA - if(running->refered_lambdas) - _ls_clear(running->refered_lambdas); -#endif /* MB_ENABLE_LAMBDA */ - - result++; - running = prev; - } - - return result; -} - -/* Dispose the scope chain */ -static int _dispose_scope_chain(mb_interpreter_t* s) { - int result = 0; - _running_context_t* running = 0; - _running_context_t* prev = 0; - - mb_assert(s); - - running = s->running_context; - while(running) { - prev = running->prev; - - _ht_foreach(running->var_dict, _destroy_object); - _ht_clear(running->var_dict); - _ht_destroy(running->var_dict); -#ifdef MB_ENABLE_LAMBDA - if(running->refered_lambdas) { - _ls_clear(running->refered_lambdas); - _ls_destroy(running->refered_lambdas); - running->refered_lambdas = 0; - } -#endif /* MB_ENABLE_LAMBDA */ - running->var_dict = 0; - mb_dispose_value(s, running->intermediate_value); - safe_free(running); - - result++; - running = prev; - } - s->running_context = 0; - - return result; -} - -/* Tidy the scope chain */ -static void _tidy_scope_chain(mb_interpreter_t* s) { - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - if(!context) - return; - - while(context->routine_state && s->running_context->meta != _SCOPE_META_ROOT) { - if(_end_routine(s)) - _pop_scope(s, false); - } -#ifdef MB_ENABLE_CLASS - while(context->class_state != _CLASS_STATE_NONE) { - if(_end_class(s)) - _pop_scope(s, false); - } -#endif /* MB_ENABLE_CLASS */ -} - -/* Tidy the intermediate value */ -static void _tidy_intermediate_value(_ref_t* ref, void* data) { - _object_t tmp; - - mb_assert(ref && data); - - if(!ref->s->running_context) - return; - - _MAKE_NIL(&tmp); - _public_value_to_internal_object(&ref->s->running_context->intermediate_value, &tmp); - if(tmp.data.usertype == data) { - switch(tmp.type) { -#ifdef MB_ENABLE_COLLECTION_LIB - case _DT_LIST: /* Fall through */ - case _DT_DICT: /* Fall through */ -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: /* Fall through */ -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_USERTYPE_REF - case _DT_USERTYPE_REF: /* Fall through */ -#endif /* MB_ENABLE_USERTYPE_REF */ - case _DT_ARRAY: - mb_make_nil(ref->s->running_context->intermediate_value); - - break; - default: /* Do nothing */ - break; - } - } -} - -/* Evaluate a variable, this is a helper function for the PRINT statement */ -static _object_t* _eval_var_in_print(mb_interpreter_t* s, _object_t** val_ptr, _ls_node_t** ast, _object_t* obj) { - _object_t tmp; - - mb_assert(s); - - switch(obj->type) { - case _DT_ROUTINE: - _execute_statement(s, ast, true); - _MAKE_NIL(&tmp); - _public_value_to_internal_object(&s->running_context->intermediate_value, &tmp); - if(tmp.type == _DT_STRING) { - tmp.data.string = mb_strdup(tmp.data.string, strlen(tmp.data.string) + 1); - tmp.ref = false; - mb_make_nil(s->running_context->intermediate_value); - } - **val_ptr = tmp; - if(*ast) *ast = (*ast)->prev; - - break; - case _DT_VAR: - *val_ptr = obj->data.variable->data; - if(*ast) *ast = (*ast)->next; - - break; - default: - *val_ptr = obj; - if(*ast) *ast = (*ast)->next; - - break; - } - - return *val_ptr; -} - -/** Interpretation */ - -/* Callback a stepped debug handler, this function is called each step */ -static void _stepped(mb_interpreter_t* s, _ls_node_t* ast) { - _object_t* obj = 0; - - mb_assert(s); - - if(s->debug_stepped_handler) { - if(ast && ast->data) { - obj = (_object_t*)ast->data; -#ifdef MB_ENABLE_SOURCE_TRACE - s->debug_stepped_handler(s, (void**)&ast, s->source_file, obj->source_pos, obj->source_row, obj->source_col); -#else /* MB_ENABLE_SOURCE_TRACE */ - s->debug_stepped_handler(s, (void**)&ast, s->source_file, obj->source_pos, 0, 0); -#endif /* MB_ENABLE_SOURCE_TRACE */ - } else { - s->debug_stepped_handler(s, (void**)&ast, s->source_file, -1, 0, 0); - } - } -} - -/* Execute the ast, this is the core execution function */ -static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l, bool_t force_next) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _running_context_t* running = 0; - _ls_node_t* sub_stack = 0; - bool_t skip_to_eoi = true; - - mb_assert(s && l); - - running = s->running_context; - sub_stack = s->sub_stack; - - ast = *l; - - obj = (_object_t*)ast->data; - -_retry: - switch(obj->type) { - case _DT_FUNC: - if(_is_binary(obj->data.func->pointer)) { - _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(_is_flow(obj->data.func->pointer)) { - result = (obj->data.func->pointer)(s, (void**)&ast); - } else { -#ifdef MB_ENABLE_STACK_TRACE - _ls_pushback(s->stack_frames, obj->data.func->name); -#endif /* MB_ENABLE_STACK_TRACE */ - result = (obj->data.func->pointer)(s, (void**)&ast); -#ifdef MB_ENABLE_STACK_TRACE - _ls_popback(s->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - } - if(result == MB_FUNC_IGNORE) { - result = MB_FUNC_OK; - obj = (_object_t*)ast->data; - - goto _retry; - } - - break; - case _DT_VAR: -#ifdef MB_ENABLE_CLASS - if(obj->data.variable->data->type == _DT_ROUTINE) { - if(ast && ast->next && _IS_FUNC(ast->next->data, _core_open_bracket)) { - obj = obj->data.variable->data; - - goto _retry; - } else { - result = _core_let(s, (void**)&ast); - } - } else if(s->last_instance || obj->data.variable->pathing) { - /* Need to path */ - _ls_node_t* pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PU(obj->data.variable->pathing), 0, 0); - if(pathed && pathed->data) { - if(obj != (_object_t*)pathed->data) { - /* Found another node */ - obj = (_object_t*)pathed->data; - - goto _retry; - } else { - /* Final node */ - result = _core_let(s, (void**)&ast); - } - } else { - /* Normal node */ - result = _core_let(s, (void**)&ast); - } - } else { - /* Do not need to path */ - result = _core_let(s, (void**)&ast); - } -#else /* MB_ENABLE_CLASS */ - result = _core_let(s, (void**)&ast); -#endif /* MB_ENABLE_CLASS */ - - break; - case _DT_ARRAY: - result = _core_let(s, (void**)&ast); - - break; - case _DT_INT: /* Fall through */ - case _DT_REAL: /* Fall through */ - case _DT_STRING: - _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - - break; -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: - _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - - break; -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - ast = ast->prev; - result = _core_call(s, (void**)&ast); - - break; -#ifdef MB_ENABLE_SOURCE_TRACE - case _DT_PREV_IMPORT: - s->source_file = obj->data.import_info->source_file; - - break; - case _DT_POST_IMPORT: - s->source_file = obj->data.import_info->source_file; - - break; -#endif /* MB_ENABLE_SOURCE_TRACE */ - default: /* Do nothing */ - break; - } - - if(s->schedule_suspend_tag) { - if(s->schedule_suspend_tag == MB_FUNC_SUSPEND) - mb_suspend(s, (void**)&ast); - result = s->schedule_suspend_tag; - s->schedule_suspend_tag = 0; - } - - if(result != MB_FUNC_OK && result != MB_FUNC_SUSPEND && result != MB_SUB_RETURN) - goto _exit; - - if(ast) { - obj = DON(ast); - if(!obj) { - /* Do nothing */ - } else if(_IS_EOS(obj)) { - if(force_next || result != MB_SUB_RETURN) - ast = ast->next; - } else if(_IS_SEP(obj, ':')) { - skip_to_eoi = false; - ast = ast->next; - } else if(obj && obj->type == _DT_VAR) { -#ifdef MB_ENABLE_CLASS - _ls_node_t* fn = 0; - if(_is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { - if(fn) { - if(s->calling) - result = _core_let(s, (void**)&ast); - } else { - _handle_error_on_obj(s, SE_RN_INVALID_ID_USAGE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - } else { - _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } -#else /* MB_ENABLE_CLASS */ - _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_CLASS */ - } else if(_IS_FUNC(obj, _core_enddef) && result != MB_SUB_RETURN) { - ast = (_ls_node_t*)_ls_popback(sub_stack); -#ifdef MB_ENABLE_LAMBDA - } else if(obj && _IS_FUNC(obj, _core_close_bracket) && s->last_routine && s->last_routine->type == _IT_LAMBDA) { - /* Do nothing */ -#endif /* MB_ENABLE_LAMBDA */ - } else if(obj && obj->type == _DT_FUNC && (_is_operator(obj->data.func->pointer) || _is_flow(obj->data.func->pointer))) { - ast = ast->next; - } else if(obj && obj->type == _DT_FUNC) { - /* Do nothing */ - } else if(obj && obj->type != _DT_FUNC) { - ast = ast->next; - } else { - _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - } - - if(skip_to_eoi && s->skip_to_eoi && s->skip_to_eoi == _ls_back(s->sub_stack)) { - s->skip_to_eoi = 0; - obj = (_object_t*)ast->data; - if(obj->type != _DT_EOS) { - result = _skip_to(s, &ast, 0, _DT_EOS); - if(result != MB_FUNC_OK) - goto _exit; - } - } - -_exit: - _destroy_lazy_objects(s); - - *l = ast; - - _stepped(s, ast); - - return result; -} - -/* Common function to end current looping */ -static int _common_end_looping(mb_interpreter_t* s, _ls_node_t** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - result = _skip_struct(s, l, _core_for, _core_next); - if(result == MB_FUNC_OK) - result = _skip_to(s, l, 0, _DT_EOS); - - return result; -} - -/* Common function to keep current looping */ -static int _common_keep_looping(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _running_context_t* running = 0; - - mb_assert(s && l); - - running = s->running_context; - ast = *l; - - obj = (_object_t*)ast->data; - while(!_IS_FUNC(obj, _core_next)) { - result = _execute_statement(s, &ast, false); - if(result == MB_LOOP_CONTINUE) { /* NEXT */ - if(!running->next_loop_var || running->next_loop_var == var_loop) { /* This loop */ - running->next_loop_var = 0; - result = MB_FUNC_OK; - - break; - } else { /* Not this loop */ - if(_skip_struct(s, &ast, _core_for, _core_next) != MB_FUNC_OK) - goto _exit; - _skip_to(s, &ast, 0, _DT_EOS); - - goto _exit; - } - } else if(result == MB_LOOP_BREAK) { /* EXIT */ - if(_skip_struct(s, &ast, _core_for, _core_next) != MB_FUNC_OK) - goto _exit; - _skip_to(s, &ast, 0, _DT_EOS); - - goto _exit; - } else if(result == MB_SUB_RETURN) { /* RETURN */ - goto _exit; - } else if(result != MB_FUNC_OK) { /* Normally */ - goto _exit; - } - - if(!ast) { - _handle_error_on_obj(s, SE_RN_NEXT_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - obj = (_object_t*)ast->data; - } - -_exit: - *l = ast; - - return result; -} - -/* Execute normal FOR-TO-STEP-NEXT-routine */ -static int _execute_normal_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* to_node = 0; - _object_t* obj = 0; - _object_t to_val; - _object_t step_val; - _object_t* to_val_ptr = 0; - _object_t* step_val_ptr = 0; - _tuple3_t ass_tuple3; - _tuple3_t* ass_tuple3_ptr = 0; - - mb_assert(s && l && var_loop); - - ast = *l; - - to_val_ptr = &to_val; - _MAKE_NIL(to_val_ptr); - step_val_ptr = &step_val; - _MAKE_NIL(step_val_ptr); - ass_tuple3_ptr = &ass_tuple3; - - /* Get begin value */ - result = _execute_statement(s, &ast, true); - if(result != MB_FUNC_OK) - goto _exit; - ast = ast->prev; - - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_to)) { - _handle_error_on_obj(s, SE_RN_TO_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - ast = ast->next; - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - to_node = ast; - -_to: - ast = to_node; - - /* Get end value */ - result = _calc_expression(s, &ast, &to_val_ptr); - if(result != MB_FUNC_OK) - goto _exit; - - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_step)) { - step_val = _OBJ_INT_UNIT; - } else { - ast = ast->next; - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - /* Get step value */ - result = _calc_expression(s, &ast, &step_val_ptr); - if(result != MB_FUNC_OK) - goto _exit; - } - - if((_compare_numbers(step_val_ptr, &_OBJ_INT_ZERO) == 1 && _compare_numbers(var_loop->data, to_val_ptr) == 1) || - (_compare_numbers(step_val_ptr, &_OBJ_INT_ZERO) == -1 && _compare_numbers(var_loop->data, to_val_ptr) == -1)) { - /* End looping */ - result = _common_end_looping(s, &ast); - - goto _exit; - } else { - /* Keep looping */ - result = _common_keep_looping(s, &ast, var_loop); - if(result == MB_LOOP_BREAK) { - result = MB_FUNC_OK; - - goto _exit; - } else if(result != MB_FUNC_OK || result == MB_SUB_RETURN) { - goto _exit; - } - - ass_tuple3.e1 = var_loop->data; - ass_tuple3.e2 = step_val_ptr; - ass_tuple3.e3 = var_loop->data; - _instruct_num_op_num(+, &ass_tuple3_ptr); - - goto _to; - } - -_exit: - *l = ast; - - return result; -} - -#ifdef MB_ENABLE_COLLECTION_LIB -/* Execute ranged FOR-IN-NEXT-routine */ -static int _execute_ranged_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { - int result = MB_FUNC_ERR; - _ls_node_t* ast = 0; - _object_t* old_val = 0; - _object_t range; - _ls_node_t* to_node = 0; - _object_t* range_ptr; - _list_it_t* lit = 0; - _dict_it_t* dit = 0; - _list_it_t* tlit = 0; - _dict_it_t* tdit = 0; - - mb_assert(s && l && var_loop); - - old_val = var_loop->data; - range_ptr = ⦥ - _MAKE_NIL(range_ptr); - - ast = *l; - ast = ast->next; - ast = ast->next; - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - /* Get collection */ - result = _calc_expression(s, &ast, &range_ptr); - if(result != MB_FUNC_OK) - goto _exit; - - /* Create iterator */ - switch(range_ptr->type) { - case _DT_LIST: - tlit = lit = _create_list_it(range_ptr->data.list, true); - - break; - case _DT_DICT: - tdit = dit = _create_dict_it(range_ptr->data.dict, true); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - - break; - } - to_node = ast; - -_to: - ast = to_node; - - /* Move next */ - if(lit) lit = _move_list_it_next(lit); - else if(dit) dit = _move_dict_it_next(dit); - if((lit && _invalid_list_it(lit)) || (dit && _invalid_dict_it(dit))) { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(!lit && !dit) { - /* End looping */ - result = _common_end_looping(s, &ast); - - goto _exit; - } else { - /* Assign loop variable */ - if(lit && !lit->list->range_begin && lit->curr.node && lit->curr.node->data) { - var_loop->data = (_object_t*)lit->curr.node->data; - } else if(lit && lit->list->range_begin) { - var_loop->data->type = _DT_INT; - var_loop->data->data.integer = lit->curr.ranging; - } else if(dit && dit->curr_node && dit->curr_node->extra) { - var_loop->data = (_object_t*)dit->curr_node->extra; - } - /* Keep looping */ - result = _common_keep_looping(s, &ast, var_loop); - if(result == MB_LOOP_BREAK) { - result = MB_FUNC_OK; - - goto _exit; - } else if(result != MB_FUNC_OK || result == MB_SUB_RETURN) { - goto _exit; - } - - goto _to; - } - -_exit: - if(tlit) _destroy_list_it(tlit); - else if(tdit) _destroy_dict_it(tdit); - switch(range_ptr->type) { _UNREF_COLL(range_ptr) default: /* Do nothing */ break; } - - *l = ast; - - var_loop->data = old_val; - - return result; -} -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* Skip current execution flow to a specific function */ -static int _skip_to(mb_interpreter_t* s, _ls_node_t** l, mb_func_t f, _data_e t) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* tmp = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - ast = *l; - mb_assert(ast && ast->prev); - do { - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(tmp), MB_FUNC_ERR, _exit, result); - } - tmp = ast; - obj = (_object_t*)ast->data; - *l = ast; - ast = ast->next; - } while(!(_IS_FUNC(obj, f)) && obj->type != t); - -_exit: - return result; -} - -/* Skip current IF execution flow to next chunk */ -static int _skip_if_chunk(mb_interpreter_t* s, _ls_node_t** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* tmp = 0; - _object_t* obj = 0; - int nested = 0; - unsigned mask = 0; - - mb_assert(s && l); - - ast = *l; - mb_assert(ast && ast->prev); - do { - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - tmp = ast; - obj = (_object_t*)ast->data; - *l = ast; - ast = ast->next; - if(ast && _IS_FUNC((_object_t*)ast->data, _core_if)) { - if(++nested > sizeof(mask) * 8) { - _handle_error_on_obj(s, SE_RN_NESTED_TOO_DEEP, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } else if(ast && nested && _IS_FUNC((_object_t*)ast->data, _core_then)) { - if(!(ast && ast->next && _IS_EOS(ast->next->data))) - mask |= 1 << (nested - 1); - } else if(ast && nested && - (((mask & (1 << (nested - 1))) && _IS_EOS(ast->data)) || - (!(mask & (1 << (nested - 1))) && _IS_FUNC((_object_t*)ast->data, _core_endif))) - ) { - if(--nested < 0) { - _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - } - } while(nested || (!_IS_FUNC(obj, _core_elseif) && !_IS_FUNC(obj, _core_else) && !_IS_FUNC(obj, _core_endif))); - -_exit: - return result; -} - -/* Skip current structure */ -static int _skip_struct(mb_interpreter_t* s, _ls_node_t** l, mb_func_t open_func, mb_func_t close_func) { - int result = MB_FUNC_OK; - int count = 0; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _object_t* obj_prev = 0; - - mb_assert(s && l && open_func && close_func); - - ast = *l; - - count = 1; - do { - if(!ast || !ast->next) { - _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - obj_prev = (_object_t*)ast->data; - ast = ast->next; - obj = (_object_t*)ast->data; - if(_IS_FUNC(obj, open_func)) - ++count; - else if(_IS_FUNC(obj, close_func) && _IS_EOS(obj_prev)) - --count; - } while(count); - -_exit: - *l = ast; - - return result; -} - -/* Create a running context */ -static _running_context_t* _create_running_context(bool_t create_var_dict) { - _running_context_t* result = 0; - - result = (_running_context_t*)mb_malloc(sizeof(_running_context_t)); - memset(result, 0, sizeof(_running_context_t)); - result->calc_depth = _INFINITY_CALC_DEPTH; - if(create_var_dict) - result->var_dict = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); - - return result; -} - -/* Reset the parsing context of a MY-BASIC environment */ -static _parsing_context_t* _reset_parsing_context(_parsing_context_t* context) { - _ls_node_t* imp = 0; - - if(!context) - context = (_parsing_context_t*)mb_malloc(sizeof(_parsing_context_t)); - else - imp = context->imported; - memset(context, 0, sizeof(_parsing_context_t)); - context->parsing_row = 1; - if(!imp) - imp = _ls_create(); - context->imported = imp; - _end_of_file(context); - - return context; -} - -/* Destroy the parsing context of a MY-BASIC environment */ -static void _destroy_parsing_context(_parsing_context_t* _mb_unaligned * context) { - if(!context || !(*context)) - return; - - if(*context) { - if((*context)->imported) { - _ls_foreach((*context)->imported, _destroy_memory); - _ls_destroy((*context)->imported); - } - safe_free(*context); - } -} - -/** Interface processors */ - -#ifdef MB_ENABLE_MODULE -/* Create a module function structure */ -static _module_func_t* _create_module_func(mb_interpreter_t* s, mb_func_t f) { - _module_func_t* result = 0; - - mb_assert(s); - - if(!s->with_module) - return result; - - result = (_module_func_t*)mb_malloc(sizeof(_module_func_t)); - result->module = mb_strdup(s->with_module, 0); - result->func = f; - - return result; -} - -/* Destroy a module function structure */ -static int _ls_destroy_module_func(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _module_func_t* mod = 0; - mb_unrefvar(extra); - - mb_assert(data); - - mod = (_module_func_t*)data; - safe_free(mod->module); - safe_free(mod); - - return result; -} - -/* Destroy all module function structures */ -static int _ht_destroy_module_func_list(void* data, void* extra) { - int result = _OP_RESULT_DEL_NODE; - _ls_node_t* lst = 0; - char* n = 0; - - mb_assert(data); - - lst = (_ls_node_t*)data; - n = (char*)extra; - _ls_foreach(lst, _ls_destroy_module_func); - _ls_destroy(lst); - safe_free(n); - - return result; -} -#endif /* MB_ENABLE_MODULE */ - -/* Generate a function name to be registered according to module information */ -static char* _generate_func_name(mb_interpreter_t* s, char* n, bool_t with_mod) { - char* name = 0; - size_t _sl = 0; - mb_unrefvar(with_mod); - - mb_assert(s && n); - - _sl = strlen(n); -#ifdef MB_ENABLE_MODULE - if(with_mod && s->with_module) { - size_t _ml = strlen(s->with_module); - name = (char*)mb_malloc(_ml + 1 + _sl + 1); - memcpy(name, s->with_module, _ml); - name[_ml] = '.'; - memcpy(name + _ml + 1, n, _sl + 1); - } else { - name = (char*)mb_malloc(_sl + 1); - memcpy(name, n, _sl + 1); - } -#else /* MB_ENABLE_MODULE */ - name = (char*)mb_malloc(_sl + 1); - memcpy(name, n, _sl + 1); -#endif /* MB_ENABLE_MODULE */ - mb_strupr(name); - - return name; -} - -/* Register a function to a MY-BASIC environment */ -static int _register_func(mb_interpreter_t* s, char* n, mb_func_t f, bool_t local) { - int result = 0; - _ht_node_t* scope = 0; - _ls_node_t* exists = 0; - char* name = 0; - - mb_assert(s); - - if(!n) - return result; - - n = mb_strdup(n, strlen(n) + 1); - mb_strupr(n); - - scope = local ? s->local_func_dict : s->global_func_dict; -#ifdef MB_ENABLE_MODULE - if(s->with_module) - name = _generate_func_name(s, n, true); -#endif /* MB_ENABLE_MODULE */ - if(!name) - name = mb_strdup(n, strlen(n) + 1); - exists = _ht_find(scope, (void*)name); - if(!exists) { - result += _ht_set_or_insert(scope, (void*)name, (void*)(intptr_t)f); - } else { - _set_current_error(s, SE_CM_FUNC_EXISTS, 0); - safe_free(name); - } - -#ifdef MB_ENABLE_MODULE - if(s->with_module) { - _ls_node_t* tmp = 0; - exists = _ht_find(s->module_func_dict, (void*)n); - if(!exists) { - name = _generate_func_name(s, n, false); - result += _ht_set_or_insert(s->module_func_dict, (void*)name, _ls_create()); - } - exists = _ht_find(s->module_func_dict, (void*)n); - exists = (_ls_node_t*)exists->data; - tmp = _ls_find(exists, s, _ls_cmp_module_func, 0); - if(!tmp) - _ls_pushback(exists, _create_module_func(s, f)); - else - _set_current_error(s, SE_CM_FUNC_EXISTS, 0); - } -#endif /* MB_ENABLE_MODULE */ - - safe_free(n); - - return result; -} - -/* Remove a function from a MY-BASIC environment */ -static int _remove_func(mb_interpreter_t* s, char* n, bool_t local) { - int result = 0; - _ht_node_t* scope = 0; - _ls_node_t* exists = 0; - char* name = 0; - - mb_assert(s); - - if(!n) - return result; - - n = mb_strdup(n, strlen(n) + 1); - mb_strupr(n); - - scope = local ? s->local_func_dict : s->global_func_dict; -#ifdef MB_ENABLE_MODULE - if(s->with_module) - name = _generate_func_name(s, n, true); -#endif /* MB_ENABLE_MODULE */ - if(!name) - name = mb_strdup(n, strlen(n) + 1); - exists = _ht_find(scope, (void*)name); - if(exists) - result += _ht_remove(scope, (void*)name, _ls_cmp_extra_string); - else - _set_current_error(s, SE_CM_FUNC_NOT_EXISTS, 0); - safe_free(name); - -#ifdef MB_ENABLE_MODULE - if(s->with_module) { - _ls_node_t* tmp = 0; - exists = _ht_find(s->module_func_dict, (void*)n); - if(exists) { - exists = (_ls_node_t*)exists->data; - tmp = _ls_find(exists, s, _ls_cmp_module_func, 0); - if(tmp) - _ls_remove(exists, tmp, _ls_destroy_module_func); - } - } -#endif /* MB_ENABLE_MODULE */ - - safe_free(n); - - return result; -} - -/* Find function interface in the function dictionaries */ -static _ls_node_t* _find_func(mb_interpreter_t* s, char* n, bool_t* mod) { - _ls_node_t* result = 0; - mb_unrefvar(mod); - - mb_assert(s && n); - - n = mb_strdup(n, strlen(n) + 1); - mb_strupr(n); - - result = _ht_find(s->local_func_dict, (void*)n); - if(!result) - result = _ht_find(s->global_func_dict, (void*)n); - -#ifdef MB_ENABLE_MODULE - if(!result) { - result = _ht_find(s->module_func_dict, (void*)n); - if(result && result->data) { - _module_func_t* mp = 0; - result = (_ls_node_t*)result->data; - result = result->next; - while(result) { - mp = (_module_func_t*)result->data; - if(_ls_find(s->using_modules, mp->module, (_ls_compare)_ht_cmp_string, 0)) - break; - result = result->next; - } - *mod = true; - } - } -#endif /* MB_ENABLE_MODULE */ - - safe_free(n); - - return result; -} - -/* Open global constants */ -static int _open_constant(mb_interpreter_t* s) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - unsigned long ul = 0; - - mb_assert(s); - - running = s->running_context; - - ul = _ht_set_or_insert(running->var_dict, MB_TRUE, _OBJ_BOOL_TRUE); - mb_assert(ul); - ul = _ht_set_or_insert(running->var_dict, MB_FALSE, _OBJ_BOOL_FALSE); - mb_assert(ul); - - return result; -} - -/* Close global constants */ -static int _close_constant(mb_interpreter_t* s) { - int result = MB_FUNC_OK; - - mb_assert(s); - - return result; -} - -/* Open the core function library */ -static int _open_core_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_core_libs); ++i) - result += _register_func(s, _core_libs[i].name, _core_libs[i].pointer, true); - - return result; -} - -/* Close the core function library */ -static int _close_core_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_core_libs); ++i) - result += _remove_func(s, _core_libs[i].name, true); - - return result; -} - -/* Open the standard function library */ -static int _open_std_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_std_libs); ++i) - result += _register_func(s, _std_libs[i].name, _std_libs[i].pointer, true); - - return result; -} - -/* Close the standard function library */ -static int _close_std_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_std_libs); ++i) - result += _remove_func(s, _std_libs[i].name, true); - - return result; -} - -#ifdef MB_ENABLE_COLLECTION_LIB -/* Open the collection function library */ -static int _open_coll_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_coll_libs); ++i) - result += _register_func(s, _coll_libs[i].name, _coll_libs[i].pointer, true); - - return result; -} - -/* Close the collection function library */ -static int _close_coll_lib(mb_interpreter_t* s) { - int result = 0; - int i = 0; - - mb_assert(s); - - for(i = 0; i < countof(_coll_libs); ++i) - result += _remove_func(s, _coll_libs[i].name, true); - - return result; -} -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* ========================================================} */ - -/* -** {======================================================== -** Public functions definitions -*/ - -/* Get the version number of this MY-BASIC system */ -unsigned mb_ver(void) { - return _MB_VERSION; -} - -/* Get the version text of this MY-BASIC system */ -const char* mb_ver_string(void) { - return _MB_VERSION_STRING; -} - -/* Initialize the MY-BASIC system */ -int mb_init(void) { - int result = MB_FUNC_OK; - - mb_assert(!_exp_assign); - _exp_assign = _create_object(); - _exp_assign->type = _DT_FUNC; - _exp_assign->data.func = (_func_t*)mb_malloc(sizeof(_func_t)); - memset(_exp_assign->data.func, 0, sizeof(_func_t)); - _exp_assign->data.func->name = (char*)mb_malloc(strlen(_DUMMY_ASSIGN_CHAR) + 1); - memcpy(_exp_assign->data.func->name, _DUMMY_ASSIGN_CHAR, strlen(_DUMMY_ASSIGN_CHAR) + 1); - _exp_assign->data.func->pointer = _core_dummy_assign; - - mb_assert(!_OBJ_BOOL_TRUE); - if(!_OBJ_BOOL_TRUE) { - _var_t* bvar = _create_var(&_OBJ_BOOL_TRUE, MB_TRUE, strlen(MB_TRUE) + 1, true); - bvar->data->type = _DT_INT; - bvar->data->data.integer = 1; - } - mb_assert(!_OBJ_BOOL_FALSE); - if(!_OBJ_BOOL_FALSE) { - _var_t* bvar = _create_var(&_OBJ_BOOL_FALSE, MB_FALSE, strlen(MB_FALSE) + 1, true); - bvar->data->type = _DT_INT; - bvar->data->data.integer = 0; - } - - return result; -} - -/* Close the MY-BASIC system */ -int mb_dispose(void) { - int result = MB_FUNC_OK; - - mb_assert(_exp_assign); - safe_free(_exp_assign->data.func->name); - safe_free(_exp_assign->data.func); - safe_free(_exp_assign); - _exp_assign = 0; - - mb_assert(_OBJ_BOOL_TRUE); - if(_OBJ_BOOL_TRUE) { - safe_free(_OBJ_BOOL_TRUE->data.variable->data); - safe_free(_OBJ_BOOL_TRUE->data.variable->name); - safe_free(_OBJ_BOOL_TRUE->data.variable); - safe_free(_OBJ_BOOL_TRUE); - _OBJ_BOOL_TRUE = 0; - } - mb_assert(_OBJ_BOOL_FALSE); - if(_OBJ_BOOL_FALSE) { - safe_free(_OBJ_BOOL_FALSE->data.variable->data); - safe_free(_OBJ_BOOL_FALSE->data.variable->name); - safe_free(_OBJ_BOOL_FALSE->data.variable); - safe_free(_OBJ_BOOL_FALSE); - _OBJ_BOOL_FALSE = 0; - } - - return result; -} - -/* Open a MY-BASIC environment */ -int mb_open(struct mb_interpreter_t** s) { - int result = MB_FUNC_OK; - _ht_node_t* local_scope = 0; - _ht_node_t* global_scope = 0; - _running_context_t* running = 0; - - mb_assert(s); - - *s = (mb_interpreter_t*)mb_malloc(sizeof(mb_interpreter_t)); - memset(*s, 0, sizeof(mb_interpreter_t)); - (*s)->valid = true; - - (*s)->in_neg_expr = _ls_create(); - - local_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ls_free_extra); - (*s)->local_func_dict = local_scope; - - global_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ls_free_extra); - (*s)->global_func_dict = global_scope; - -#ifdef MB_ENABLE_MODULE - global_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ht_destroy_module_func_list); - (*s)->module_func_dict = global_scope; - (*s)->using_modules = _ls_create(); -#endif /* MB_ENABLE_MODULE */ - - (*s)->parsing_context = _reset_parsing_context((*s)->parsing_context); - - (*s)->edge_destroy_objects = _ls_create(); - (*s)->lazy_destroy_objects = _ls_create(); - - (*s)->gc.table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); - (*s)->gc.recursive_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); - (*s)->gc.collected_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); - (*s)->gc.valid_table = 0; - (*s)->gc.collecting = 0; - - running = _create_running_context(true); - running->meta = _SCOPE_META_ROOT; - (*s)->running_context = running; - -#ifdef MB_ENABLE_STACK_TRACE - (*s)->stack_frames = _ls_create(); -#endif /* MB_ENABLE_STACK_TRACE */ - - (*s)->sub_stack = _ls_create(); - - (*s)->ast = _ls_create(); - - _open_core_lib(*s); - _open_std_lib(*s); -#ifdef MB_ENABLE_COLLECTION_LIB - _open_coll_lib(*s); -#endif /* MB_ENABLE_COLLECTION_LIB */ - - result = _open_constant(*s); - mb_assert(MB_FUNC_OK == result); - - return result; -} - -/* Close a MY-BASIC environment */ -int mb_close(struct mb_interpreter_t** s) { - int result = MB_FUNC_OK; - _ht_node_t* local_scope = 0; - _ht_node_t* global_scope = 0; - _ls_node_t* ast; - - mb_assert(s); - - (*s)->valid = false; - -#ifdef MB_ENABLE_COLLECTION_LIB - _close_coll_lib(*s); -#endif /* MB_ENABLE_COLLECTION_LIB */ - _close_std_lib(*s); - _close_core_lib(*s); - - ast = (*s)->ast; - _ls_foreach(ast, _destroy_object); - _ls_destroy(ast); - - _ls_destroy((*s)->sub_stack); - -#ifdef MB_ENABLE_STACK_TRACE - _ls_destroy((*s)->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - - _tidy_scope_chain(*s); - _dispose_scope_chain(*s); - - _gc_collect_garbage(*s, -1); - _ht_destroy((*s)->gc.table); - _ht_destroy((*s)->gc.recursive_table); - _ht_destroy((*s)->gc.collected_table); - (*s)->gc.table = 0; - (*s)->gc.recursive_table = 0; - (*s)->gc.collected_table = 0; - - _ls_foreach((*s)->edge_destroy_objects, _destroy_object); - _ls_destroy((*s)->edge_destroy_objects); - _ls_foreach((*s)->lazy_destroy_objects, _destroy_object); - _ls_destroy((*s)->lazy_destroy_objects); - - _destroy_parsing_context(&(*s)->parsing_context); - -#ifdef MB_ENABLE_MODULE - global_scope = (*s)->module_func_dict; - _ht_foreach(global_scope, _ht_destroy_module_func_list); - _ht_destroy(global_scope); - _ls_foreach((*s)->using_modules, _destroy_memory); - _ls_destroy((*s)->using_modules); -#endif /* MB_ENABLE_MODULE */ - - global_scope = (*s)->global_func_dict; - _ht_foreach(global_scope, _ls_free_extra); - _ht_destroy(global_scope); - - local_scope = (*s)->local_func_dict; - _ht_foreach(local_scope, _ls_free_extra); - _ht_destroy(local_scope); - - _ls_destroy((*s)->in_neg_expr); - - _close_constant(*s); - - safe_free(*s); - - return result; -} - -/* Reset a MY-BASIC environment */ -int mb_reset(struct mb_interpreter_t** s, bool_t clrf) { - int result = MB_FUNC_OK; - _ht_node_t* global_scope = 0; - _ls_node_t* ast; - _parsing_context_t* context = 0; - _running_context_t* running = 0; - - mb_assert(s); - - (*s)->jump_set = _JMP_NIL; - (*s)->last_routine = 0; - (*s)->no_eat_comma_mark = 0; - (*s)->last_error = SE_NO_ERR; - (*s)->last_error_file = 0; - - running = (*s)->running_context; - _ls_clear((*s)->sub_stack); - (*s)->suspent_point = 0; - running->next_loop_var = 0; - memset(&(running->intermediate_value), 0, sizeof(mb_value_t)); - - (*s)->parsing_context = context = _reset_parsing_context((*s)->parsing_context); - - ast = (*s)->ast; - _ls_foreach(ast, _destroy_object); - _ls_clear(ast); - -#ifdef MB_ENABLE_STACK_TRACE - _ls_clear((*s)->stack_frames); -#endif /* MB_ENABLE_STACK_TRACE */ - - _clear_scope_chain(*s); - - if(clrf) { -#ifdef MB_ENABLE_MODULE - global_scope = (*s)->module_func_dict; - _ht_foreach(global_scope, _ht_destroy_module_func_list); - _ht_clear(global_scope); - _ls_foreach((*s)->using_modules, _destroy_memory); - _ls_clear((*s)->using_modules); -#endif /* MB_ENABLE_MODULE */ - - global_scope = (*s)->global_func_dict; - _ht_foreach(global_scope, _ls_free_extra); - _ht_clear(global_scope); - } - - result = _open_constant(*s); - mb_assert(MB_FUNC_OK == result); - - return result; -} - -/* Register an API function to a MY-BASIC environment */ -int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f) { - mb_assert(s && n && f); - - return _register_func(s, (char*)n, f, false); -} - -/* Remove an API function from a MY-BASIC environment */ -int mb_remove_func(struct mb_interpreter_t* s, const char* n) { - mb_assert(s && n); - - return _remove_func(s, (char*)n, false); -} - -/* Remove a reserved API from a MY-BASIC environment */ -int mb_remove_reserved_func(struct mb_interpreter_t* s, const char* n) { - mb_assert(s && n); - - return _remove_func(s, (char*)n, true); -} - -/* Begin a module, all functions registered within a module will put inside it */ -int mb_begin_module(struct mb_interpreter_t* s, const char* n) { - int result = MB_FUNC_OK; - - mb_assert(s && n); - -#ifdef MB_ENABLE_MODULE - if(s->with_module) { - _handle_error_on_obj(s, SE_RN_MODULE_NOT_MATCH, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); - } else { - s->with_module = mb_strdup(n, strlen(n) + 1); - } -#else /* MB_ENABLE_MODULE */ - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result); -#endif /* MB_ENABLE_MODULE */ - -_exit: - return result; -} - -/* End a module */ -int mb_end_module(struct mb_interpreter_t* s) { - int result = MB_FUNC_OK; - - mb_assert(s); - -#ifdef MB_ENABLE_MODULE - if(s->with_module) { - safe_free(s->with_module); - } else { - _handle_error_on_obj(s, SE_RN_MODULE_NOT_MATCH, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); - } - -#else /* MB_ENABLE_MODULE */ - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result); -#endif /* MB_ENABLE_MODULE */ - -_exit: - return result; -} - -/* Try attempting to begin an API function */ -int mb_attempt_func_begin(struct mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - obj = (_object_t*)ast->data; - if(!(obj->type == _DT_FUNC)) { - _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - - ++s->no_eat_comma_mark; - -_exit: - *l = ast; - - return result; -} - -/* Try attempting to end an API function */ -int mb_attempt_func_end(struct mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - --s->no_eat_comma_mark; - - return result; -} - -/* Try attempting an open bracket */ -int mb_attempt_open_bracket(struct mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - ast = ast->next; - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_open_bracket)) { - _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - -_exit: - *l = ast; - - return result; -} - -/* Try attempting a close bracket */ -int mb_attempt_close_bracket(struct mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - if(!ast) { - _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_close_bracket)) { - _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - -_exit: - *l = ast; - - return result; -} - -/* Detect if there is any more argument */ -int mb_has_arg(struct mb_interpreter_t* s, void** l) { - int result = 0; - _ls_node_t* ast = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - if(ast) { - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_close_bracket) && obj->type != _DT_EOS) - result = obj->type != _DT_SEP && obj->type != _DT_EOS; - } - - return result; -} - -/* Pop an integer argument */ -int mb_pop_int(struct mb_interpreter_t* s, void** l, int_t* val) { - int result = MB_FUNC_OK; - mb_value_t arg; - int_t tmp = 0; - - mb_assert(s && l && val); - - mb_make_nil(arg); - - mb_check(mb_pop_value(s, l, &arg)); - - switch(arg.type) { - case MB_DT_INT: - tmp = arg.value.integer; - - break; - case MB_DT_REAL: - tmp = (int_t)(arg.value.float_point); - - break; - default: - result = MB_FUNC_ERR; - - goto _exit; - } - - *val = tmp; - -_exit: - return result; -} - -/* Pop a float point argument */ -int mb_pop_real(struct mb_interpreter_t* s, void** l, real_t* val) { - int result = MB_FUNC_OK; - mb_value_t arg; - real_t tmp = 0; - - mb_assert(s && l && val); - - mb_make_nil(arg); - - mb_check(mb_pop_value(s, l, &arg)); - - switch(arg.type) { - case MB_DT_INT: - tmp = (real_t)(arg.value.integer); - - break; - case MB_DT_REAL: - tmp = arg.value.float_point; - - break; - default: - result = MB_FUNC_ERR; - - goto _exit; - } - - *val = tmp; - -_exit: - return result; -} - -/* Pop a string argument */ -int mb_pop_string(struct mb_interpreter_t* s, void** l, char** val) { - int result = MB_FUNC_OK; - mb_value_t arg; - char* tmp = 0; - - mb_assert(s && l && val); - - mb_make_nil(arg); - - mb_check(mb_pop_value(s, l, &arg)); - - switch(arg.type) { - case MB_DT_STRING: - tmp = arg.value.string; - - break; - default: - result = MB_FUNC_ERR; - - goto _exit; - } - - *val = tmp; - -_exit: - return result; -} - -/* Pop a usertype argument */ -int mb_pop_usertype(struct mb_interpreter_t* s, void** l, void** val) { - int result = MB_FUNC_OK; - mb_value_t arg; - void* tmp = 0; - - mb_assert(s && l && val); - - mb_make_nil(arg); - - mb_check(mb_pop_value(s, l, &arg)); - - switch(arg.type) { - case MB_DT_USERTYPE: - tmp = arg.value.usertype; - - break; - default: - result = MB_FUNC_ERR; - - goto _exit; - } - - *val = tmp; - -_exit: - return result; -} - -/* Pop an argument value */ -int mb_pop_value(struct mb_interpreter_t* s, void** l, mb_value_t* val) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t val_obj; - _object_t* val_ptr = 0; - _running_context_t* running = 0; - int* inep = 0; - - mb_assert(s && l && val); - - running = s->running_context; - - if(!_ls_empty(s->in_neg_expr)) - inep = (int*)_ls_back(s->in_neg_expr)->data; - - val_ptr = &val_obj; - _MAKE_NIL(val_ptr); - - ast = (_ls_node_t*)*l; - result = _calc_expression(s, &ast, &val_ptr); - if(result != MB_FUNC_OK) - goto _exit; - - if(val_ptr->type == _DT_STRING && !val_ptr->ref) { - _destroy_edge_objects(s); - _mark_edge_destroy_string(s, val_ptr->data.string); - } - _REF(val_ptr) - - if(s->no_eat_comma_mark < _NO_EAT_COMMA && (!inep || (inep && !(*inep)))) { - if(ast && _IS_SEP(ast->data, ',')) - ast = ast->next; - } - - result = _internal_object_to_public_value(val_ptr, val); - if(result != MB_FUNC_OK) - goto _exit; - -_exit: - *l = ast; - - return result; -} - -/* Push an integer argument */ -int mb_push_int(struct mb_interpreter_t* s, void** l, int_t val) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_int(arg, val); - mb_check(mb_push_value(s, l, arg)); - - return result; -} - -/* Push a float point argument */ -int mb_push_real(struct mb_interpreter_t* s, void** l, real_t val) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_real(arg, val); - mb_convert_to_int_if_posible(arg); - mb_check(mb_push_value(s, l, arg)); - - return result; -} - -/* Push a string argument */ -int mb_push_string(struct mb_interpreter_t* s, void** l, char* val) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_string(arg, val); - mb_check(mb_push_value(s, l, arg)); - _mark_lazy_destroy_string(s, val); - - return result; -} - -/* Push a usertype argument */ -int mb_push_usertype(struct mb_interpreter_t* s, void** l, void* val) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_usertype(arg, val); - mb_check(mb_push_value(s, l, arg)); - - return result; -} - -/* Push an argument value */ -int mb_push_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _object_t obj; - - mb_assert(s && l); - - running = s->running_context; - _assign_public_value(&running->intermediate_value, &val); - - _MAKE_NIL(&obj); - _public_value_to_internal_object(&running->intermediate_value, &obj); - _REF(&obj) - - _gc_try_trigger(s); - - return result; -} - -/* Begin a class */ -int mb_begin_class(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t** meta, int c, mb_value_t* out) { -#ifdef MB_ENABLE_CLASS - int result = MB_FUNC_OK; - _class_t* instance = 0; - _object_t* obj = 0; - _running_context_t* running = 0; - _ls_node_t* tmp = 0; - _var_t* var = 0; - int i = 0; - _object_t mo; - _class_t* mi = 0; - - mb_assert(s && l && n && out); - - running = s->running_context; - - tmp = (_ls_node_t*)*l; - - _using_jump_set_of_structured(s, tmp, _exit, result); - - tmp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); - if(tmp && tmp->data) { - obj = (_object_t*)tmp->data; - if(obj->type == _DT_VAR) - var = obj->data.variable; - } - if(s->last_instance || (obj && !var)) { - _handle_error_on_obj(s, SE_RN_DUPLICATE_CLASS, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - obj = _create_object(); - obj->type = _DT_CLASS; - - instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_class(s, instance, mb_strdup(n, strlen(n) + 1)); - - for(i = 0; i < c; i++) { - if(meta[i]->type != MB_DT_CLASS) { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _MAKE_NIL(&mo); - _public_value_to_internal_object(meta[i], &mo); - mi = mo.data.instance; - _link_meta_class(s, instance, mi); - } - - _push_scope_by_class(s, instance->scope); - obj->data.instance = instance; - - if(var) { - _destroy_object(var->data, 0); - var->data = obj; - } else { - _ht_set_or_insert(running->var_dict, (void*)n, obj); - } - - s->last_instance = instance; - -_exit: - return result; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(n); - mb_unrefvar(meta); - mb_unrefvar(c); - mb_unrefvar(out); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_CLASS */ -} - -/* End a class */ -int mb_end_class(struct mb_interpreter_t* s, void** l) { -#ifdef MB_ENABLE_CLASS - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _pop_scope(s, false); - - s->last_instance = 0; - - return result; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(s); - mb_unrefvar(l); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_CLASS */ -} - -/* Get the userdata of a class instance */ -int mb_get_class_userdata(struct mb_interpreter_t* s, void** l, void** d) { -#ifdef MB_ENABLE_CLASS - int result = MB_FUNC_OK; - - mb_assert(s && l && d); - - if(s && s->last_instance) { - if(d) - *d = s->last_instance->userdata; - } else if(s && s->last_routine && s->last_routine->instance) { - if(d) - *d = s->last_routine->instance->userdata; - } else { - if(d) *d = 0; - - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -_exit: - return result; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(d); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_CLASS */ -} - -/* Set the userdata of a class instance */ -int mb_set_class_userdata(struct mb_interpreter_t* s, void** l, void* d) { -#ifdef MB_ENABLE_CLASS - int result = MB_FUNC_OK; - - mb_assert(s && l && d); - - if(s && s->last_instance) { - s->last_instance->userdata = d; - } else { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -_exit: - return result; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(d); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_CLASS */ -} - -/* Get a value by its identifier name */ -int mb_get_value_by_name(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) { - int result = MB_FUNC_OK; - _ls_node_t* tmp = 0; - _object_t* obj = 0; - - mb_assert(s && l && n); - - mb_make_nil(*val); - - tmp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NORMAL, 0, 0); - if(tmp && tmp->data) { - obj = (_object_t*)tmp->data; - _internal_object_to_public_value(obj, val); - } - - return result; -} - -/* Add a variable with a specific name */ -int mb_add_var(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t val, bool_t force) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _object_t* obj = 0; - _var_t* var = 0; - _ls_node_t* tmp = 0; - - mb_assert(s && l && n); - - running = s->running_context; - - tmp = _ht_find(running->var_dict, (void*)n); - - if(tmp) { - if(force) { - result = mb_set_var_value(s, tmp->data, val); - - goto _exit; - } else { - _handle_error_on_obj(s, SE_RN_DUPLICATE_ID, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - - var = _create_var(&obj, n, strlen(n) + 1, true); - _public_value_to_internal_object(&val, var->data); - - _ht_set_or_insert(running->var_dict, var->name, obj); - -_exit: - return result; -} - -/* Get a token literally, store it in an argument if it's a variable */ -int mb_get_var(struct mb_interpreter_t* s, void** l, void** v) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - - mb_assert(s && l); - - if(v) *v = 0; - - ast = (_ls_node_t*)*l; - if(ast) { - obj = (_object_t*)ast->data; - if(_IS_SEP(obj, ',')) { - ast = ast->next; - obj = (_object_t*)ast->data; - } - ast = ast->next; - } - - if(obj && obj->type == _DT_VAR) { - if(v) - *v = obj; - } - - if(ast && _IS_SEP(ast->data, ',')) - ast = ast->next; - - *l = ast; - - return result; -} - -/* Get the value of a variable */ -int mb_get_var_value(struct mb_interpreter_t* s, void* v, mb_value_t* val) { - int result = MB_FUNC_OK; - _object_t* obj = 0; - - mb_assert(s); - - if(!val || !v) - goto _exit; - - obj = (_object_t*)v; - if(obj->type != _DT_VAR) - goto _exit; - - _internal_object_to_public_value(obj->data.variable->data, val); - -_exit: - return result; -} - -/* Set the value of a variable */ -int mb_set_var_value(struct mb_interpreter_t* s, void* v, mb_value_t val) { - int result = MB_FUNC_OK; - _object_t* obj = 0; - - mb_assert(s); - - if(!v) - goto _exit; - obj = (_object_t*)v; - if(obj->type != _DT_VAR) - goto _exit; - - _public_value_to_internal_object(&val, obj->data.variable->data); - -_exit: - return result; -} - -/* Create an array */ -int mb_init_array(struct mb_interpreter_t* s, void** l, mb_data_e t, int* d, int c, void** a) { - int result = MB_FUNC_OK; - _array_t* arr = 0; - _data_e type = _DT_NIL; - int j = 0; - int n = 0; - mb_unrefvar(t); - - mb_assert(s && l && d && a); - - *a = 0; - if(c >= MB_MAX_DIMENSION_COUNT) { - _handle_error_on_obj(s, SE_RN_TOO_MANY_DIMENSIONS, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - for(j = 0; j < c; j++) { - n = d[j]; - if(n <= 0) { - _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - -#ifdef MB_SIMPLE_ARRAY - if(t == MB_DT_REAL) { - type = _DT_REAL; - } else if(t == MB_DT_STRING) { - type = _DT_STRING; - } else { - _handle_error_on_obj(s, SE_RN_NEED_COMPLEX_ARRAY, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } -#else /* MB_SIMPLE_ARRAY */ - type = _DT_REAL; -#endif /* MB_SIMPLE_ARRAY */ - - arr = _create_array(s, 0, type); - for(j = 0; j < c; j++) { - n = d[j]; - arr->dimensions[arr->dimension_count++] = n; - if(arr->count) - arr->count *= (unsigned)n; - else - arr->count += (unsigned)n; - } - _init_array(arr); - if(!arr->raw) { - arr->dimension_count = 0; - arr->dimensions[0] = 0; - arr->count = 0; - } - *a = arr; - -_exit: - return result; -} - -/* Get the length of an array */ -int mb_get_array_len(struct mb_interpreter_t* s, void** l, void* a, int r, int* i) { - int result = MB_FUNC_OK; - _array_t* arr = 0; - - mb_assert(s && l); - - arr = (_array_t*)a; - if(r < 0 || r >= arr->dimension_count) { - _handle_error_on_obj(s, SE_RN_RANK_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - if(i) - *i = arr->dimensions[r]; - -_exit: - return result; -} - -/* Get an element of an array with a specific index */ -int mb_get_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t* val) { - int result = MB_FUNC_OK; - _array_t* arr = 0; - int index = 0; - _data_e type = _DT_NIL; - - mb_assert(s && l); - - arr = (_array_t*)a; - if(c < 0 || c > arr->dimension_count) { - _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - if(!val) - goto _exit; - - index = _get_array_pos(s, arr, d, c); - if(index < 0) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - _get_array_elem(s, arr, index, &val->value, &type); - val->type = _internal_type_to_public_type(type); - -_exit: - return result; -} - -/* Set an element of an array with a specific index */ -int mb_set_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t val) { - int result = MB_FUNC_OK; - _array_t* arr = 0; - int index = 0; - _data_e type = _DT_NIL; - - mb_assert(s && l); - - arr = (_array_t*)a; - if(c < 0 || c > arr->dimension_count) { - _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - index = _get_array_pos(s, arr, d, c); - if(index < 0) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - type = _public_type_to_internal_type(val.type); - _set_array_elem(s, 0, arr, (unsigned)index, &val.value, &type); - -_exit: - return result; -} - -/* Initialize a collection */ -int mb_init_coll(struct mb_interpreter_t* s, void** l, mb_value_t* coll) { - int result = MB_FUNC_OK; - - mb_assert(s); - -#ifdef MB_ENABLE_COLLECTION_LIB - switch(coll->type) { - case MB_DT_LIST: - coll->value.list = _create_list(s); - - break; - case MB_DT_DICT: - coll->value.dict = _create_dict(s); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(coll); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - return result; -} - -/* Get an element of a collection */ -int mb_get_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t* val) { - int result = MB_FUNC_OK; - _object_t ocoll; - int_t i = 0; - mb_value_t ret; - - mb_assert(s); - - mb_make_nil(ret); - - _MAKE_NIL(&ocoll); -#ifdef MB_ENABLE_COLLECTION_LIB - switch(coll.type) { - case MB_DT_LIST: - mb_int_val(idx, i); - _public_value_to_internal_object(&coll, &ocoll); - if(!_at_list(ocoll.data.list, i, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - if(!_find_dict(ocoll.data.dict, &idx, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(idx); - mb_unrefvar(coll); - mb_unrefvar(i); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - if(val) *val = ret; - - return result; -} - -/* Set an element of a collection */ -int mb_set_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t val) { - int result = MB_FUNC_OK; - _object_t ocoll; - int_t i = 0; - _object_t* oval = 0; - mb_value_t ret; - - mb_assert(s); - - mb_make_nil(ret); - - _MAKE_NIL(&ocoll); -#ifdef MB_ENABLE_COLLECTION_LIB - switch(coll.type) { - case MB_DT_LIST: - mb_int_val(idx, i); - _public_value_to_internal_object(&coll, &ocoll); - while((int)ocoll.data.list->count <= i) - _push_list(ocoll.data.list, &ret, 0); - if(!_set_list(ocoll.data.list, i, &val, &oval)) { - _destroy_object(oval, 0); - - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - _set_dict(ocoll.data.dict, &idx, &val, 0, 0); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(val); - mb_unrefvar(idx); - mb_unrefvar(coll); - mb_unrefvar(oval); - mb_unrefvar(i); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - return result; -} - -/* Remove an element from a collection */ -int mb_remove_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx) { - int result = MB_FUNC_OK; - _object_t ocoll; - int_t i = 0; - mb_value_t ret; - - mb_assert(s); - - mb_make_nil(ret); - - _MAKE_NIL(&ocoll); -#ifdef MB_ENABLE_COLLECTION_LIB - switch(coll.type) { - case MB_DT_LIST: - mb_int_val(idx, i); - _public_value_to_internal_object(&coll, &ocoll); - if(!_remove_at_list(ocoll.data.list, i)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - if(!_remove_dict(ocoll.data.dict, &idx)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(coll); - mb_unrefvar(idx); - mb_unrefvar(i); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - return result; -} - -/* Tell the element count of a collection */ -int mb_count_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, int* c) { - int result = MB_FUNC_OK; - _object_t ocoll; -#ifdef MB_ENABLE_COLLECTION_LIB - _list_t* lst = 0; - _dict_t* dct = 0; -#endif /* MB_ENABLE_COLLECTION_LIB */ - int ret = 0; - - mb_assert(s); - - _MAKE_NIL(&ocoll); -#ifdef MB_ENABLE_COLLECTION_LIB - switch(coll.type) { - case MB_DT_LIST: - lst = (_list_t*)coll.value.list; - ret = (int)lst->count; - - break; - case MB_DT_DICT: - dct = (_dict_t*)coll.value.dict; - ret = (int)_ht_count(dct->dict); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(coll); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - if(c) *c = ret; - - return result; -} - -/* Create a referenced usertype value */ -int mb_make_ref_value(struct mb_interpreter_t* s, void* val, mb_value_t* out, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft) { -#ifdef MB_ENABLE_USERTYPE_REF - int result = MB_FUNC_OK; - _usertype_ref_t* ref = 0; - - mb_assert(s && out); - - if(out) { - ref = _create_usertype_ref(s, val, un, cl, hs, cp, ft); - out->type = MB_DT_USERTYPE_REF; - out->value.usertype_ref = ref; - } - - return result; -#else /* MB_ENABLE_USERTYPE_REF */ - mb_unrefvar(s); - mb_unrefvar(val); - mb_unrefvar(out); - mb_unrefvar(un); - mb_unrefvar(cl); - mb_unrefvar(hs); - mb_unrefvar(cp); - mb_unrefvar(ft); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_USERTYPE_REF */ -} - -/* Get the data of a referenced usertype value */ -int mb_get_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val, void** out) { -#ifdef MB_ENABLE_USERTYPE_REF - int result = MB_FUNC_OK; - _usertype_ref_t* ref = 0; - - mb_assert(s && out); - - if(val.type != MB_DT_USERTYPE_REF) { - _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - if(out) { - ref = (_usertype_ref_t*)val.value.usertype_ref; - *out = ref->usertype; - } - -_exit: - return result; -#else /* MB_ENABLE_USERTYPE_REF */ - mb_unrefvar(s); - mb_unrefvar(l); - mb_unrefvar(val); - mb_unrefvar(out); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_USERTYPE_REF */ -} - -/* Increase the reference of a value by 1 */ -int mb_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { - int result = MB_FUNC_OK; - _object_t obj; - - mb_assert(s && l); - - _MAKE_NIL(&obj); - _public_value_to_internal_object(&val, &obj); - if( -#ifdef MB_ENABLE_USERTYPE_REF - obj.type != _DT_USERTYPE_REF && -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_COLLECTION_LIB - obj.type != _DT_LIST && obj.type != _DT_DICT && -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - obj.type != _DT_CLASS && -#endif /* MB_ENABLE_CLASS */ - obj.type != _DT_ARRAY - ) { - _handle_error_on_obj(s, SE_RN_REFERENCED_TYPE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _REF(&obj); - -_exit: - return result; -} - -/* Decrease the reference of a value by 1 */ -int mb_unref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { - int result = MB_FUNC_OK; - _object_t obj; - - mb_assert(s && l); - - _MAKE_NIL(&obj); - _public_value_to_internal_object(&val, &obj); - if( -#ifdef MB_ENABLE_USERTYPE_REF - obj.type != _DT_USERTYPE_REF && -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifdef MB_ENABLE_COLLECTION_LIB - obj.type != _DT_LIST && obj.type != _DT_DICT && -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - obj.type != _DT_CLASS && -#endif /* MB_ENABLE_CLASS */ - obj.type != _DT_ARRAY - ) { - _handle_error_on_obj(s, SE_RN_REFERENCED_TYPE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _UNREF(&obj); - -_exit: - return result; -} - -/* Override a meta function of a value */ -int mb_override_value(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_meta_func_u m, void* f) { - int result = MB_FUNC_OK; - _object_t obj; - - mb_assert(s && l); - -#ifdef MB_ENABLE_USERTYPE_REF - _MAKE_NIL(&obj); - if(val.type == MB_DT_USERTYPE_REF) { - _usertype_ref_t* user = 0; - _public_value_to_internal_object(&val, &obj); - user = obj.data.usertype_ref; - if(m & MB_MF_CALC) { - if(!user->calc_operators) { - user->calc_operators = (_calculation_operator_info_t*)mb_malloc(sizeof(_calculation_operator_info_t)); - memset(user->calc_operators, 0, sizeof(_calculation_operator_info_t)); - } - } - switch(m) { - case MB_MF_IS: - user->calc_operators->is = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_ADD: - user->calc_operators->add = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_SUB: - user->calc_operators->sub = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_MUL: - user->calc_operators->mul = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_DIV: - user->calc_operators->div = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_NEG: - user->calc_operators->neg = (mb_meta_operator_t)(intptr_t)f; - - break; - case MB_MF_COLL: - user->coll_func = (mb_meta_func_t)(intptr_t)f; - - break; - case MB_MF_FUNC: - user->generic_func = (mb_meta_func_t)(intptr_t)f; - - break; - default: /* Do nothing */ - break; - } - } else { - result = MB_FUNC_ERR; - } -#else /* MB_ENABLE_USERTYPE_REF */ - mb_unrefvar(obj); - mb_unrefvar(val); - mb_unrefvar(m); - mb_unrefvar(f); -#endif /* MB_ENABLE_USERTYPE_REF */ - - return result; -} - -/* Dispose a value */ -int mb_dispose_value(struct mb_interpreter_t* s, mb_value_t val) { - int result = MB_FUNC_OK; - - mb_assert(s); - - if(val.type == MB_DT_STRING) - safe_free(val.value.string); - - _assign_public_value(&val, 0); - - return result; -} - -/* Get a sub routine with a specific name */ -int mb_get_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) { - int result = MB_FUNC_OK; - _object_t* obj = 0; - _ls_node_t* scp = 0; - - mb_assert(s && l && n && val); - - mb_make_nil(*val); - - scp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); - if(scp) { - obj = (_object_t*)scp->data; - if(obj) { - if(obj->type == _DT_ROUTINE) { - _internal_object_to_public_value(obj, val); - } else { - _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - } - -_exit: - return result; -} - -/* Set a sub routine with a specific name and native function pointer */ -int mb_set_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_routine_func_t f, bool_t force) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _object_t* obj = 0; - _routine_t* routine = 0; - _ls_node_t* tmp = 0; - _var_t* var = 0; - - mb_assert(s && l); - - running = s->running_context; - - tmp = _ht_find(running->var_dict, (void*)n); - - if(tmp) { - if(force) { - obj = (_object_t*)tmp->data; - if(obj->type == _DT_VAR) - var = obj->data.variable; - } else { - _handle_error_on_obj(s, SE_RN_DUPLICATE_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - - routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); - _init_routine(s, routine, mb_strdup(n, strlen(n) + 1), f); - - obj = _create_object(); - obj->type = _DT_ROUTINE; - obj->data.routine = routine; - obj->ref = false; - -#ifdef MB_ENABLE_CLASS - routine->instance = s->last_instance; -#endif /* MB_ENABLE_CLASS */ - - if(var && force) { - _destroy_object(var->data, 0); - var->data = obj; - } else { - _ht_set_or_insert(running->var_dict, routine->name, obj); - } - -_exit: - return result; -} - -/* Evaluate a sub routine */ -int mb_eval_routine(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_value_t* args, unsigned argc) { - int result = MB_FUNC_OK; - _object_t obj; - - if(val.type != MB_DT_ROUTINE) { - _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - _MAKE_NIL(&obj); - _public_value_to_internal_object(&val, &obj); - result = _eval_routine(s, (_ls_node_t**)l, args, argc, obj.data.routine, _has_routine_fun_arg, _pop_routine_fun_arg); - -_exit: - return result; -} - -/* Load and parse a script string */ -int mb_load_string(struct mb_interpreter_t* s, const char* l, bool_t reset) { - int result = MB_FUNC_OK; - int status = 0; - unsigned short _row = 0; - unsigned short _col = 0; - char wrapped = _ZERO_CHAR; - _parsing_context_t* context = 0; - - mb_assert(s); - - if(!s->parsing_context) - s->parsing_context = _reset_parsing_context(s->parsing_context); - - context = s->parsing_context; - - mb_uu_getbom(&l); - while(*l) { - int n = 1; -#ifdef MB_ENABLE_UNICODE_ID - if(context->parsing_state == _PS_NORMAL) - n = mb_uu_ischar(l); -#endif /* MB_ENABLE_UNICODE_ID */ - do { - if(n == 1) { - char ch = *l; - if((ch == _NEWLINE_CHAR || ch == _RETURN_CHAR) && (!wrapped || wrapped == ch)) { - unsigned short before = 0; - wrapped = ch; - before = context->parsing_row++; - context->parsing_col = 0; - if(before > context->parsing_row) { - context->parsing_col = 1; - _handle_error_now(s, SE_RN_PROGRAM_TOO_LONG, s->last_error_file, MB_FUNC_ERR); - - goto _exit; - } - - break; - } - } - wrapped = _ZERO_CHAR; - ++context->parsing_col; - } while(0); - status = _parse_char(s, l, n, context->parsing_pos, _row, _col); - result = status; - if(status) { - _set_error_pos(s, context->parsing_pos, _row, _col); - _handle_error_now(s, s->last_error, s->last_error_file, result); - - goto _exit; - } - _row = context->parsing_row; - _col = context->parsing_col; - ++context->parsing_pos; - l += n; - }; - status = _parse_char(s, 0, 1, context->parsing_pos, context->parsing_row, context->parsing_col); - -_exit: - if(reset) - _end_of_file(context); - - return result; -} - -/* Load and parse a script file */ -int mb_load_file(struct mb_interpreter_t* s, const char* f) { - int result = MB_FUNC_OK; - char* buf = 0; - _parsing_context_t* context = 0; - - mb_assert(s); - - context = s->parsing_context; - - s->parsing_context = context = _reset_parsing_context(s->parsing_context); - - buf = _load_file(s, f, 0); - if(buf) { - result = mb_load_string(s, buf, true); - safe_free(buf); - - if(result) - goto _exit; - } else { - _set_current_error(s, SE_PS_OPEN_FILE_FAILED, 0); - - result = MB_FUNC_ERR; - } - -_exit: - context->parsing_state = _PS_NORMAL; - - return result; -} - -/* Run the current AST */ -int mb_run(struct mb_interpreter_t* s) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - - mb_assert(s); - - if(s->parsing_context) { - if(s->parsing_context->routine_state) { - result = MB_FUNC_ERR; - _handle_error_now(s, SE_RN_INCOMPLETE_ROUTINE, s->source_file, result); - - goto _exit; - } -#ifdef MB_ENABLE_CLASS - if(s->parsing_context->class_state != _CLASS_STATE_NONE) { - result = MB_FUNC_ERR; - _handle_error_now(s, SE_RN_INCOMPLETE_CLASS, s->source_file, result); - - goto _exit; - } -#endif /* MB_ENABLE_CLASS */ - } - - _destroy_parsing_context(&s->parsing_context); - - s->handled_error = false; - - if(s->suspent_point) { - ast = s->suspent_point; - ast = ast->next; - s->suspent_point = 0; - } else { - s->source_file = 0; -#ifdef MB_ENABLE_CLASS - s->last_instance = 0; - s->calling = false; -#endif /* MB_ENABLE_CLASS */ - s->last_routine = 0; - - mb_assert(!s->no_eat_comma_mark); - while(s->running_context->prev) - s->running_context = s->running_context->prev; - ast = s->ast; - ast = ast->next; - if(!ast) { - result = MB_FUNC_ERR; - _set_error_pos(s, 0, 0, 0); - _handle_error_now(s, SE_RN_EMPTY_PROGRAM, s->source_file, result); - - goto _exit; - } - } - - do { - result = _execute_statement(s, &ast, true); - if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { - if(result != MB_FUNC_SUSPEND && s->error_handler) { - if(result >= MB_EXTENDED_ABORT) - s->last_error = SE_EA_EXTENDED_ABORT; - _handle_error_now(s, s->last_error, s->last_error_file, result); - } - - goto _exit; - } - } while(ast); - -_exit: - _destroy_edge_objects(s); - - return result; -} - -/* Suspend current execution and save the context */ -int mb_suspend(struct mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - - mb_assert(s && l && *l); - - ast = (_ls_node_t*)*l; - s->suspent_point = ast; - - return result; -} - -/* Schedule to suspend current execution */ -int mb_schedule_suspend(struct mb_interpreter_t* s, int t) { - int result = MB_FUNC_OK; - - mb_assert(s); - - if(t == MB_FUNC_OK) - t = MB_FUNC_SUSPEND; - s->schedule_suspend_tag = t; - - return result; -} - -/* Get the value of an identifier */ -int mb_debug_get(struct mb_interpreter_t* s, const char* n, mb_value_t* val) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _ls_node_t* v = 0; - _object_t* obj = 0; - mb_value_t tmp; - - mb_assert(s && n); - - running = s->running_context; - - v = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); - if(v) { - obj = (_object_t*)v->data; - mb_assert(obj->type == _DT_VAR); - if(val) - result = _internal_object_to_public_value(obj->data.variable->data, val); - else - result = _internal_object_to_public_value(obj->data.variable->data, &tmp); - } else { - if(val) { - mb_make_nil(*val); - } - _handle_error_on_obj(s, SE_RN_DEBUG_ID_NOT_FOUND, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); - } - -_exit: - return result; -} - -/* Set the value of an identifier */ -int mb_debug_set(struct mb_interpreter_t* s, const char* n, mb_value_t val) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _ls_node_t* v = 0; - _object_t* obj = 0; - - mb_assert(s && n); - - running = s->running_context; - - v = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); - if(v) { - obj = (_object_t*)v->data; - mb_assert(obj->type == _DT_VAR); - result = _public_value_to_internal_object(&val, obj->data.variable->data); - } else { - _handle_error_on_obj(s, SE_RN_DEBUG_ID_NOT_FOUND, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); - } - -_exit: - return result; -} - -/* Get stack frame names of a MY-BASIC environment */ -int mb_debug_get_stack_trace(struct mb_interpreter_t* s, void** l, char** fs, unsigned fc) { -#ifdef MB_ENABLE_STACK_TRACE - int result = MB_FUNC_OK; - _ls_node_t* f = 0; - unsigned i = 0; - mb_unrefvar(l); - - mb_assert(s); - - if(fs && fc) { - if(_ls_count(s->stack_frames) > fc) - fs[--fc] = "..."; - f = s->stack_frames->prev; - while(f != s->stack_frames && f && f->data && i < fc) { - fs[i++] = (char*)f->data; - f = f->prev; - } - } - while(i < fc) - fs[i++] = 0; - - return result; -#else /* MB_ENABLE_STACK_TRACE */ - int result = MB_FUNC_OK; - mb_unrefvar(fs); - mb_unrefvar(fc); - - _handle_error_on_obj(s, SE_RN_STACK_TRACE_DISABLED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - -_exit: - return result; -#endif /* MB_ENABLE_STACK_TRACE */ -} - -/* Set a stepped handler to a MY-BASIC environment */ -int mb_debug_set_stepped_handler(struct mb_interpreter_t* s, mb_debug_stepped_handler_t h) { - int result = MB_FUNC_OK; - - mb_assert(s); - - s->debug_stepped_handler = h; - - return result; -} - -/* Get type description text */ -const char* mb_get_type_string(mb_data_e t) { - switch(t) { - case MB_DT_NIL: - return "NIL"; - case MB_DT_UNKNOWN: - return "UNKNOWN"; - case MB_DT_INT: - return "INTEGER"; - case MB_DT_REAL: - return "REAL"; - case MB_DT_NUM: - return "NUMBER"; - case MB_DT_STRING: - return "STRING"; - case MB_DT_TYPE: - return "TYPE"; - case MB_DT_USERTYPE: - return "USERTYPE"; -#ifdef MB_ENABLE_USERTYPE_REF - case MB_DT_USERTYPE_REF: - return "USERTYPE_REF"; -#endif /* MB_ENABLE_USERTYPE_REF */ - case MB_DT_ARRAY: - return "ARRAY"; -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - return "LIST"; - case MB_DT_LIST_IT: - return "LIST_ITERATOR"; - case MB_DT_DICT: - return "DICT"; - case MB_DT_DICT_IT: - return "DICT_ITERATOR"; - case MB_DT_COLLECTION: - return "COLLECTION"; - case MB_DT_ITERATOR: - return "ITERATOR"; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: - return "CLASS"; -#endif /* MB_ENABLE_CLASS */ - case MB_DT_ROUTINE: - return "ROUTINE"; - default: /* Return a not exist string */ - return ""; - } -} - -/* Raise an error */ -int mb_raise_error(struct mb_interpreter_t* s, void** l, mb_error_e err, int ret) { - int result = MB_FUNC_ERR; - - mb_assert(s); - - _handle_error_on_obj(s, err, s->source_file, DON2(l), ret, _exit, result); - -_exit: - return result; -} - -/* Get the last error information */ -mb_error_e mb_get_last_error(struct mb_interpreter_t* s) { - mb_error_e result = SE_NO_ERR; - - mb_assert(s); - - result = s->last_error; - s->last_error = SE_NO_ERR; - - return result; -} - -/* Get the error description text */ -const char* mb_get_error_desc(mb_error_e err) { - if(err < countof(_ERR_DESC)) - return _ERR_DESC[err]; - - return ""; -} - -/* Set an error handler to a MY-BASIC environment */ -int mb_set_error_handler(struct mb_interpreter_t* s, mb_error_handler_t h) { - int result = MB_FUNC_OK; - - mb_assert(s); - - s->error_handler = h; - - return result; -} - -/* Set a print functor to a MY-BASIC environment */ -int mb_set_printer(struct mb_interpreter_t* s, mb_print_func_t p) { - int result = MB_FUNC_OK; - - mb_assert(s); - - s->printer = p; - - return result; -} - -/* Set an input functor to a MY-BASIC environment */ -int mb_set_inputer(struct mb_interpreter_t* s, mb_input_func_t p) { - int result = MB_FUNC_OK; - - mb_assert(s); - - s->inputer = p; - - return result; -} - -/* Trigger GC */ -int mb_gc(struct mb_interpreter_t* s, int_t* collected) { - int_t diff = 0; - - mb_assert(s); - - diff = (int_t)_mb_allocated; - _gc_collect_garbage(s, 1); - diff = (int_t)(_mb_allocated - diff); - if(collected) - *collected = diff; - - return MB_FUNC_OK; -} - -/* Get the userdata of a MY-BASIC environment */ -int mb_get_userdata(struct mb_interpreter_t* s, void** d) { - int result = MB_FUNC_OK; - - mb_assert(s && d); - - if(s && d) - *d = s->userdata; - - return result; -} - -/* Set the userdata of a MY-BASIC environment */ -int mb_set_userdata(struct mb_interpreter_t* s, void* d) { - int result = MB_FUNC_OK; - - mb_assert(s); - - if(s) - s->userdata = d; - - return result; -} - -/* Set an import handler to a MY-BASIC environment */ -int mb_set_import_handler(struct mb_interpreter_t* s, mb_import_handler_t h) { - int result = MB_FUNC_OK; - - mb_assert(s); - - s->import_handler = h; - - return result; -} - -/* Safe stdin reader function */ -int mb_gets(char* buf, int s) { - int result = 0; - - if(fgets(buf, s, stdin) == 0) { - fprintf(stderr, "Error reading.\n"); - - exit(1); - } - result = (int)strlen(buf); - if(buf[result - 1] == _NEWLINE_CHAR) { - buf[result - 1] = _ZERO_CHAR; - result--; - } - - return result; -} - -/* Duplicate a string for internal use */ -char* mb_memdup(const char* val, unsigned size) { - char* result = 0; - - if(val != 0) { - result = (char*)mb_malloc(size); - memcpy(result, val, size); - } - - return result; -} - -/* Register an allocator and a freer globally */ -int mb_set_memory_manager(mb_memory_allocate_func_t a, mb_memory_free_func_t f) { - _mb_allocate_func = a; - _mb_free_func = f; - - return MB_FUNC_OK; -} - -/* ========================================================} */ - -/* -** {======================================================== -** Lib definitions -*/ - -/** Core lib */ - -/* Operator #, dummy assignment */ -static int _core_dummy_assign(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* Operator + */ -static int _core_add(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_obj_meta_obj(add, l, result, _exit); - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_connect_strings(l); - } else { - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); - } - } else { - _instruct_num_op_num(+, l); - } - -_exit: - return result; -} - -/* Operator - */ -static int _core_min(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_obj_meta_obj(sub, l, result, _exit); - _instruct_num_op_num(-, l); - - goto _exit; /* Avoid an unreferenced label warning */ - -_exit: - return result; -} - -/* Operator * */ -static int _core_mul(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_obj_meta_obj(mul, l, result, _exit); - _instruct_num_op_num(*, l); - - goto _exit; /* Avoid an unreferenced label warning */ - -_exit: - return result; -} - -/* Operator / */ -static int _core_div(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_obj_meta_obj(div, l, result, _exit); - _proc_div_by_zero(s, l, _exit, result, SE_RN_DIVIDE_BY_ZERO); - _instruct_num_op_num(/, l); - -_exit: - return result; -} - -/* Operator MOD */ -static int _core_mod(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _proc_div_by_zero(s, l, _exit, result, SE_RN_MOD_BY_ZERO); - _instruct_int_op_int(%, l); - -_exit: - return result; -} - -/* Operator ^ */ -static int _core_pow(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_fun_num_num(pow, l); - - return result; -} - -/* Operator ( */ -static int _core_open_bracket(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* Operator ) */ -static int _core_close_bracket(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - -#ifdef MB_ENABLE_LAMBDA - if(s->last_routine && s->last_routine->type == _IT_LAMBDA) { - result = _core_return(s, l); - - goto _exit; - } -#endif /* MB_ENABLE_LAMBDA */ - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* Operator - (negative) */ -static int _core_neg(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - mb_value_t arg; - _running_context_t* running = 0; - int* inep = 0; - int calc_depth = 0; -#ifdef MB_ENABLE_USERTYPE_REF - _object_t obj; -#endif /* MB_ENABLE_USERTYPE_REF */ - - mb_assert(s && l); - - running = s->running_context; - ast = (_ls_node_t*)*l; - if(ast) ast = ast->next; - - if(!_ls_empty(s->in_neg_expr)) - inep = (int*)_ls_back(s->in_neg_expr)->data; - - if(inep) - (*inep)++; - - calc_depth = running->calc_depth; - - mb_make_nil(arg); - if(ast && _IS_FUNC((_object_t*)ast->data, _core_open_bracket)) { - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - } else { - running->calc_depth = 1; - - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_func_end(s, l)); - } - - if(inep) - (*inep)--; - - switch(arg.type) { - case MB_DT_INT: - arg.value.integer = -(arg.value.integer); - - break; - case MB_DT_REAL: - arg.value.float_point = -(arg.value.float_point); - - break; -#ifdef MB_ENABLE_USERTYPE_REF - case MB_DT_USERTYPE_REF: - _MAKE_NIL(&obj); - _public_value_to_internal_object(&arg, &obj); - if(obj.data.usertype_ref->calc_operators && obj.data.usertype_ref->calc_operators->neg) { - mb_meta_operator_t neg = obj.data.usertype_ref->calc_operators->neg; - mb_check(mb_ref_value(s, l, arg)); - mb_check(mb_unref_value(s, l, arg)); - mb_check(neg(s, l, &arg, 0, &arg)); - - break; - } - /* Fall through */ -#endif /* MB_ENABLE_USERTYPE_REF */ - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, arg)); - -_exit: - running->calc_depth = calc_depth; - - return result; -} - -/* Operator = (equal) */ -static int _core_equal(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(==, l); - } else { - _set_tuple3_result(l, 0); - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(==, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(==, l); - } - -_exit: - return result; -} - -/* Operator < */ -static int _core_less(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(<, l); - } else { - if(_is_string(((_tuple3_t*)*l)->e1)) { - _set_tuple3_result(l, 0); - } else { - _set_tuple3_result(l, 1); - } - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(<, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(<, l); - } - -_exit: - return result; -} - -/* Operator > */ -static int _core_greater(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(>, l); - } else { - if(_is_string(((_tuple3_t*)*l)->e1)) { - _set_tuple3_result(l, 1); - } else { - _set_tuple3_result(l, 0); - } - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(>, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(>, l); - } - -_exit: - return result; -} - -/* Operator <= */ -static int _core_less_equal(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(<=, l); - } else { - if(_is_string(((_tuple3_t*)*l)->e1)) { - _set_tuple3_result(l, 0); - } else { - _set_tuple3_result(l, 1); - } - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(<=, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(<=, l); - } - -_exit: - return result; -} - -/* Operator >= */ -static int _core_greater_equal(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(>=, l); - } else { - if(_is_string(((_tuple3_t*)*l)->e1)) { - _set_tuple3_result(l, 1); - } else { - _set_tuple3_result(l, 0); - } - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(>=, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(>=, l); - } - -_exit: - return result; -} - -/* Operator <> */ -static int _core_not_equal(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _tuple3_t* tpr = 0; - - mb_assert(s && l); - - if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { - if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { - _instruct_compare_strings(!=, l); - } else { - _set_tuple3_result(l, 1); - _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); - } - } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { - _instruct_num_op_num(!=, l); - tpr = (_tuple3_t*)*l; - if(((_object_t*)tpr->e3)->type != _DT_INT) { - ((_object_t*)tpr->e3)->type = _DT_INT; - ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; - } - } else { - _instruct_obj_op_obj(!=, l); - } - -_exit: - return result; -} - -/* Operator AND */ -static int _core_and(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_bool_op_bool(&&, l); - - return result; -} - -/* Operator OR */ -static int _core_or(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - _instruct_bool_op_bool(||, l); - - return result; -} - -/* Operator NOT */ -static int _core_not(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - mb_value_t arg; - _running_context_t* running = 0; - int calc_depth = 0; - - mb_assert(s && l); - - running = s->running_context; - ast = (_ls_node_t*)*l; - if(ast) ast = ast->next; - - calc_depth = running->calc_depth; - - mb_make_nil(arg); - if(ast && _IS_FUNC((_object_t*)ast->data, _core_open_bracket)) { - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - } else { - running->calc_depth = 1; - - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_func_end(s, l)); - } - - switch(arg.type) { - case MB_DT_NIL: - mb_make_bool(arg, true); - - break; - case MB_DT_INT: - mb_make_bool(arg, !arg.value.integer); - - break; - case MB_DT_REAL: - mb_make_bool(arg, arg.value.float_point == (real_t)0); - - break; - default: - mb_make_bool(arg, false); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - - running->calc_depth = calc_depth; - - return result; -} - -/* Operator IS */ -static int _core_is(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _object_t* fst = 0; - _object_t* scd = 0; - _object_t* val = 0; - bool_t is_a = 0; - - mb_assert(s && l); - - _instruct_obj_meta_obj(is, l, result, _exit); - - fst = (_object_t*)((_tuple3_t*)*l)->e1; - scd = (_object_t*)((_tuple3_t*)*l)->e2; - val = (_object_t*)((_tuple3_t*)*l)->e3; - - if(fst && fst->type == _DT_VAR) fst = fst->data.variable->data; - if(scd && scd->type == _DT_VAR) scd = scd->data.variable->data; - if(!fst || !scd) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); - } - if(scd->type == _DT_TYPE) { - val->type = _DT_INT; - val->data.integer = (int_t)(!!(_internal_type_to_public_type(fst->type) & scd->data.type)); - } else { -#ifdef MB_ENABLE_CLASS - if(!_IS_CLASS(fst) || !_IS_CLASS(scd)) { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); - } - _traverse_class(fst->data.instance, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, scd->data.instance, &is_a); - val->type = _DT_INT; - val->data.integer = (int_t)is_a; -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(is_a); - - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); -#endif /* MB_ENABLE_CLASS */ - } - -_exit: - return result; -} - -/* LET statement */ -static int _core_let(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _running_context_t* running = 0; - _var_t* var = 0; - _var_t* evar = 0; - int refc = 1; - _array_t* arr = 0; - _object_t* arr_obj = 0; - unsigned arr_idx = 0; - bool_t literally = false; - _object_t* val = 0; - bool_t freed = false; -#ifdef MB_ENABLE_COLLECTION_LIB - int_t idx = 0; - mb_value_t key; - bool_t is_coll = false; -#endif /* MB_ENABLE_COLLECTION_LIB */ - - mb_assert(s && l); - - running = s->running_context; - - ast = (_ls_node_t*)*l; - obj = (_object_t*)ast->data; - if(obj->type == _DT_FUNC) - ast = ast->next; - if(!ast || !ast->data) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - obj = (_object_t*)ast->data; -#ifdef MB_ENABLE_CLASS - if(_IS_VAR(obj)) { - _ls_node_t* fn = 0; - if(_is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { - if(fn) - obj = (_object_t*)fn->data; - } - } -#endif /* MB_ENABLE_CLASS */ - if(obj->type == _DT_ARRAY) { - arr_obj = obj; - arr = _search_array_in_scope_chain(s, obj->data.array, &arr_obj); - result = _get_array_index(s, &ast, 0, &arr_idx, &literally); - if(result != MB_FUNC_OK) - goto _exit; - } else if(obj->type == _DT_VAR && obj->data.variable->data->type == _DT_ARRAY) { - arr_obj = obj->data.variable->data; - arr = _search_array_in_scope_chain(s, obj->data.variable->data->data.array, &arr_obj); - result = _get_array_index(s, &ast, 0, &arr_idx, &literally); - if(result != MB_FUNC_OK) - goto _exit; - } else if(obj->type == _DT_VAR) { - if(_IS_ME(obj->data.variable)) { - _handle_error_on_obj(s, SE_RN_CANNOT_CHANGE_ME, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } else { - evar = obj->data.variable; - var = _search_var_in_scope_chain(s, obj->data.variable); - if(var == evar) evar = 0; - } - } else { - _handle_error_on_obj(s, SE_RN_VAR_OR_ARRAY_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - ast = ast->next; - if(!ast || !ast->data) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } -#ifdef MB_ENABLE_COLLECTION_LIB - if(var && _IS_COLL(var->data)) { - obj = (_object_t*)ast->data; - if(_IS_FUNC(obj, _core_open_bracket)) { - mb_check(mb_attempt_open_bracket(s, l)); - - switch(var->data->type) { - case _DT_LIST: - mb_check(mb_pop_int(s, l, &idx)); - - break; - case _DT_DICT: - mb_make_nil(key); - mb_check(mb_pop_value(s, l, &key)); - - break; - default: /* Do nothing */ - break; - } - - mb_check(mb_attempt_close_bracket(s, l)); - - ast = (_ls_node_t*)*l; - is_coll = true; - } - } -#endif /* MB_ENABLE_COLLECTION_LIB */ - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_equal)) { /* Is it an assign operator? */ - _handle_error_on_obj(s, SE_RN_ASSIGN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - ast = ast->next; - val = _create_object(); - result = _calc_expression(s, &ast, &val); - - if(var) { -#ifdef MB_ENABLE_COLLECTION_LIB - if(is_coll) { - switch(var->data->type) { - case _DT_LIST: - if(!_set_list(var->data->data.list, idx, 0, &val)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case _DT_DICT: - _set_dict(var->data->data.dict, &key, 0, 0, val); - - break; - default: /* Do nothing */ - break; - } - - goto _exit; - } -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - if(evar && evar->pathing && evar->data->type == _DT_STRING && !(var->data->type == _DT_STRING && evar->data->data.string != var->data->data.string)) { - _dispose_object(evar->data); - evar = 0; - freed = true; - } -_proc_extra_var: -#endif /* MB_ENABLE_CLASS */ - if(!freed) - _dispose_object(var->data); - var->data->type = val->type; -#ifdef MB_ENABLE_COLLECTION_LIB - if(val->type == _DT_LIST_IT || val->type == _DT_DICT_IT) - _assign_with_it(var->data, val); - else - var->data->data = val->data; -#else /* MB_ENABLE_COLLECTION_LIB */ - var->data->data = val->data; -#endif /* MB_ENABLE_COLLECTION_LIB */ - if(val->type == _DT_ROUTINE) { -#ifdef MB_ENABLE_LAMBDA - if(val->data.routine->type == _IT_LAMBDA) - var->data->ref = val->ref; - else - var->data->ref = 1; -#else /* MB_ENABLE_LAMBDA */ - var->data->ref = 1; -#endif /* MB_ENABLE_LAMBDA */ - } else { - var->data->ref = val->ref; - } -#ifdef MB_ENABLE_CLASS - if(evar && evar->pathing) { - var = evar; - evar = 0; - refc++; - - goto _proc_extra_var; - } -#endif /* MB_ENABLE_CLASS */ - } else if(arr && literally) { - if(val->type != _DT_UNKNOWN) { -#ifdef MB_ENABLE_ARRAY_REF - _unref(&arr_obj->data.array->ref, arr_obj->data.array); -#endif /* MB_ENABLE_ARRAY_REF */ - arr_obj->type = val->type; -#ifdef MB_ENABLE_COLLECTION_LIB - if(val->type == _DT_LIST_IT || val->type == _DT_DICT_IT) - _assign_with_it(arr_obj, val); - else - arr_obj->data = val->data; -#else /* MB_ENABLE_COLLECTION_LIB */ - arr_obj->data = val->data; -#endif /* MB_ENABLE_COLLECTION_LIB */ - arr_obj->ref = val->ref; - } - } else if(arr) { - mb_value_u _val; - switch(val->type) { - case _DT_NIL: /* Fall through */ - case _DT_INT: /* Fall through */ - case _DT_REAL: /* Fall through */ - case _DT_STRING: /* Fall through */ - case _DT_USERTYPE: - _COPY_BYTES(_val.bytes, val->data.bytes); - - break; - default: - safe_free(val); - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - result = _set_array_elem(s, ast, arr, arr_idx, &_val, &val->type); - if(result != MB_FUNC_OK) - goto _exit; - if(val->type == _DT_STRING && !val->ref) { - safe_free(val->data.string); - } - } - while(refc--) { - _REF(val) - } - safe_free(val); - -_exit: - *l = ast; - - return result; -} - -/* DIM statement */ -static int _core_dim(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* arr = 0; - _object_t* len = 0; - mb_value_u val; - _array_t dummy; - - mb_assert(s && l); - - /* Array name */ - ast = (_ls_node_t*)*l; - if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_ARRAY) { - _handle_error_on_obj(s, SE_RN_ARRAY_IDENTIFIER_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - arr = (_object_t*)ast->data; - memset(&dummy, 0, sizeof(_array_t)); - dummy.type = arr->data.array->type; - dummy.name = arr->data.array->name; - /* ( */ - if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_FUNC || ((_object_t*)ast->next->data)->data.func->pointer != _core_open_bracket) { - _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - /* Array subscript */ - if(!ast->next) { - _handle_error_on_obj(s, SE_RN_ARRAY_SUBSCRIPT_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - while(((_object_t*)ast->data)->type != _DT_FUNC || ((_object_t*)ast->data)->data.func->pointer != _core_close_bracket) { - /* Get an integer value */ - len = (_object_t*)ast->data; - if(!_try_get_value(len, &val, _DT_INT)) { - _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(val.integer <= 0) { - _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(dummy.dimension_count >= MB_MAX_DIMENSION_COUNT) { - _handle_error_on_obj(s, SE_RN_TOO_MANY_DIMENSIONS, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - dummy.dimensions[dummy.dimension_count++] = (int)val.integer; - if(dummy.count) - dummy.count *= (unsigned)val.integer; - else - dummy.count += (unsigned)val.integer; - ast = ast->next; - /* Comma? */ - if(_IS_SEP(ast->data, ',')) - ast = ast->next; - } - /* Create or modify raw data */ - _clear_array(arr->data.array); -#ifdef MB_ENABLE_ARRAY_REF - dummy.ref = arr->data.array->ref; -#endif /* MB_ENABLE_ARRAY_REF */ - *(arr->data.array) = dummy; - _init_array(arr->data.array); - if(!arr->data.array->raw) { - arr->data.array->dimension_count = 0; - arr->data.array->dimensions[0] = 0; - arr->data.array->count = 0; - _handle_error_on_obj(s, SE_RN_OUT_OF_MEMORY, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - -_exit: - *l = ast; - - return result; -} - -/* IF statement */ -static int _core_if(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* val = 0; - _object_t* obj = 0; - bool_t multi_line = false; - bool_t skip = false; - _running_context_t* running = 0; - - mb_assert(s && l); - - running = s->running_context; - - ast = (_ls_node_t*)*l; - ast = ast->next; - - val = _create_object(); - -_elseif: - _MAKE_NIL(val); - result = _calc_expression(s, &ast, &val); - if(result != MB_FUNC_OK) - goto _exit; - - obj = (_object_t*)ast->data; - if(val->data.integer) { - skip = true; - - if(!_IS_FUNC(obj, _core_then)) { - _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - if(ast && ast->next && _IS_EOS(ast->next->data)) - multi_line = true; - else - s->skip_to_eoi = _ls_back(s->sub_stack); - do { - ast = ast->next; - while(ast && _IS_EOS(ast->data)) - ast = ast->next; - if(ast && _IS_FUNC(ast->data, _core_endif)) { - ast = ast->prev; - - break; - } - result = _execute_statement(s, &ast, true); - if(result != MB_FUNC_OK) - goto _exit; - if(ast) - ast = ast->prev; - } while(ast && ( - (!multi_line && _IS_SEP(ast->data, ':')) || ( - multi_line && ast->next && ( - !_IS_FUNC(ast->next->data, _core_elseif) && - !_IS_FUNC(ast->next->data, _core_else) && - !_IS_FUNC(ast->next->data, _core_endif) - ) - ) - ) - ); - - if(!ast) - goto _exit; - - obj = (_object_t*)ast->data; - if(obj->type != _DT_EOS) { - s->skip_to_eoi = 0; - result = _skip_to(s, &ast, 0, _DT_EOS); - if(result != MB_FUNC_OK) - goto _exit; - } - } else { - if(ast && ast->next && _IS_EOS(ast->next->data)) { - multi_line = true; - - _skip_if_chunk(s, &ast); - } - if(multi_line && ast && _IS_FUNC(ast->data, _core_elseif)) { - ast = ast->next; - - goto _elseif; - } - if(multi_line && ast && _IS_FUNC(ast->data, _core_endif)) - goto _exit; - - result = _skip_to(s, &ast, _core_else, _DT_EOS); - if(result != MB_FUNC_OK) - goto _exit; - - obj = (_object_t*)ast->data; - if(obj->type != _DT_EOS) { - skip = true; - - if(!_IS_FUNC(obj, _core_else)) { - _handle_error_on_obj(s, SE_RN_ELSE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - do { - ast = ast->next; - while(_IS_EOS(ast->data)) - ast = ast->next; - if(ast && _IS_FUNC(ast->data, _core_endif)) { - ast = ast->prev; - - break; - } - result = _execute_statement(s, &ast, true); - if(result != MB_FUNC_OK) - goto _exit; - if(ast) - ast = ast->prev; - } while(ast && ( - (!multi_line && _IS_SEP(ast->data, ':')) || - (multi_line && !_IS_FUNC(ast->next->data, _core_endif)) - ) - ); - } - } - -_exit: - if(result == MB_SUB_RETURN) { - if(ast) - ast = ast->prev; - } else { - if(multi_line) { - int ret = MB_FUNC_OK; - if(skip) - ret = _skip_struct(s, &ast, _core_if, _core_endif); - if(result != MB_FUNC_END && result != MB_LOOP_BREAK && result != MB_LOOP_CONTINUE && result != MB_SUB_RETURN) - result = ret; - } - } - - *l = ast; - - if(val->type != _DT_UNKNOWN) - _destroy_object(val, 0); - - return result; -} - -/* THEN statement */ -static int _core_then(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* ELSEIF statement */ -static int _core_elseif(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* ELSE statement */ -static int _core_else(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* ENDIF statement */ -static int _core_endif(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* FOR statement */ -static int _core_for(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _var_t* var_loop = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - ast = ast->next; - - obj = (_object_t*)ast->data; - if(obj->type != _DT_VAR) { - _handle_error_on_obj(s, SE_RN_LOOP_VAR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - var_loop = obj->data.variable; - -#ifdef MB_ENABLE_COLLECTION_LIB - if(ast && ast->next && _IS_FUNC(ast->next->data, _core_in)) - result = _execute_ranged_for_loop(s, &ast, var_loop); - else - result = _execute_normal_for_loop(s, &ast, var_loop); -#else /* MB_ENABLE_COLLECTION_LIB */ - result = _execute_normal_for_loop(s, &ast, var_loop); -#endif /* MB_ENABLE_COLLECTION_LIB */ - -_exit: - *l = ast; - - return result; -} - -/* IN statement */ -static int _core_in(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* TO statement */ -static int _core_to(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* STEP statement */ -static int _core_step(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* NEXT statement */ -static int _core_next(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _running_context_t* running = 0; - - mb_assert(s && l); - - running = s->running_context; - ast = (_ls_node_t*)*l; - - result = MB_LOOP_CONTINUE; - - ast = ast->next; - if(ast && ((_object_t*)ast->data)->type == _DT_VAR) { - obj = (_object_t*)ast->data; - running->next_loop_var = obj->data.variable; - } - - *l = ast; - - return result; -} - -/* WHILE statement */ -static int _core_while(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* loop_begin_node = 0; - _object_t* obj = 0; - _object_t loop_cond; - _object_t* loop_cond_ptr = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - ast = ast->next; - - loop_cond_ptr = &loop_cond; - _MAKE_NIL(loop_cond_ptr); - - loop_begin_node = ast; - -_loop_begin: - ast = loop_begin_node; - - result = _calc_expression(s, &ast, &loop_cond_ptr); - if(result != MB_FUNC_OK) - goto _exit; - - if(loop_cond_ptr->data.integer) { - /* Keep looping */ - obj = (_object_t*)ast->data; - while(!_IS_FUNC(obj, _core_wend)) { - result = _execute_statement(s, &ast, true); - if(result == MB_LOOP_BREAK) { /* EXIT */ - if(_skip_struct(s, &ast, _core_while, _core_wend) != MB_FUNC_OK) - goto _exit; - _skip_to(s, &ast, 0, _DT_EOS); - result = MB_FUNC_OK; - - goto _exit; - } else if(result == MB_SUB_RETURN && s->last_routine) { /* RETURN */ - if(ast) ast = ast->prev; - if(ast) ast = ast->prev; - - goto _exit; - } else if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { /* Normally */ - goto _exit; - } - - if(!ast) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - obj = (_object_t*)ast->data; - } - - goto _loop_begin; - } else { - /* End looping */ - if(_skip_struct(s, &ast, _core_while, _core_wend) != MB_FUNC_OK) - goto _exit; - _skip_to(s, &ast, 0, _DT_EOS); - - goto _exit; - } - -_exit: - *l = ast; - - return result; -} - -/* WEND statement */ -static int _core_wend(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* DO statement */ -static int _core_do(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* loop_begin_node = 0; - _object_t* obj = 0; - _object_t loop_cond; - _object_t* loop_cond_ptr = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - ast = ast->next; - - obj = (_object_t*)ast->data; - if(!_IS_EOS(obj)) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - - loop_cond_ptr = &loop_cond; - _MAKE_NIL(loop_cond_ptr); - - loop_begin_node = ast; - -_loop_begin: - ast = loop_begin_node; - - obj = (_object_t*)ast->data; - while(!_IS_FUNC(obj, _core_until)) { - result = _execute_statement(s, &ast, true); - if(result == MB_LOOP_BREAK) { /* EXIT */ - if(_skip_struct(s, &ast, _core_do, _core_until) != MB_FUNC_OK) - goto _exit; - _skip_to(s, &ast, 0, _DT_EOS); - result = MB_FUNC_OK; - - goto _exit; - } else if(result == MB_SUB_RETURN && s->last_routine) { /* RETURN */ - if(ast) ast = ast->prev; - if(ast) ast = ast->prev; - - goto _exit; - } else if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { /* Normally */ - goto _exit; - } - - obj = (_object_t*)ast->data; - } - - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_until)) { - _handle_error_on_obj(s, SE_RN_UNTIL_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - - result = _calc_expression(s, &ast, &loop_cond_ptr); - if(result != MB_FUNC_OK) - goto _exit; - - if(loop_cond_ptr->data.integer) { - /* End looping */ - if(ast) - _skip_to(s, &ast, 0, _DT_EOS); - - goto _exit; - } else { - /* Keep looping */ - goto _loop_begin; - } - -_exit: - *l = ast; - - return result; -} - -/* UNTIL statement */ -static int _core_until(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* EXIT statement */ -static int _core_exit(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - result = MB_LOOP_BREAK; - - return result; -} - -/* GOTO statement */ -static int _core_goto(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _running_context_t* running = 0; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _label_t* label = 0; - _ls_node_t* glbsyminscope = 0; - - mb_assert(s && l); - - running = s->running_context; - - ast = (_ls_node_t*)*l; - ast = ast->next; - - _using_jump_set_of_instructional(s, ast, _exit, result); - - obj = (_object_t*)ast->data; - if(obj->type != _DT_LABEL) { - _handle_error_on_obj(s, SE_RN_JUMP_LABEL_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - label = (_label_t*)obj->data.label; - if(!label->node) { - glbsyminscope = _ht_find(running->var_dict, label->name); - if(!(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_LABEL)) { - _handle_error_on_obj(s, SE_RN_LABEL_NOT_EXISTS, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - label->node = ((_object_t*)glbsyminscope->data)->data.label->node; - } - - mb_assert(label->node && label->node->prev); - ast = label->node->prev; - if(ast && !ast->data) - ast = ast->next; - -_exit: - *l = ast; - - return result; -} - -/* GOSUB statement */ -static int _core_gosub(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - - mb_assert(s && l); - - running = s->running_context; - ast = (_ls_node_t*)*l; - - _using_jump_set_of_instructional(s, ast, _exit, result); - - result = _core_goto(s, l); - if(result == MB_FUNC_OK) - _ls_pushback(s->sub_stack, ast); - -_exit: - return result; -} - -/* RETURN statement */ -static int _core_return(mb_interpreter_t* s, void** l) { - int result = MB_SUB_RETURN; - _ls_node_t* ast = 0; - _ls_node_t* sub_stack = 0; - _running_context_t* running = 0; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - running = s->running_context; - sub_stack = s->sub_stack; - - if(running->prev) { - ast = (_ls_node_t*)*l; - ast = ast->next; - if(mb_has_arg(s, (void**)&ast)) { - mb_check(mb_pop_value(s, (void**)&ast, &arg)); - mb_check(mb_push_value(s, (void**)&ast, arg)); - } - } - ast = (_ls_node_t*)_ls_popback(sub_stack); - if(!ast) { - _handle_error_on_obj(s, SE_RN_NO_RETURN_POINT, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - *l = ast; - -_exit: - return result; -} - -/* CALL statement */ -static int _core_call(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _routine_t* routine = 0; - mb_value_t ret; - _ls_node_t* pathed = 0; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - ast = ast->next; - - obj = (_object_t*)ast->data; - -_retry: - switch(obj->type) { - case _DT_FUNC: - if(_IS_FUNC(obj, _core_open_bracket)) { - mb_check(mb_attempt_open_bracket(s, l)); - - ast = ast->next; - obj = (_object_t*)ast->data; -#ifdef MB_ENABLE_CLASS - if(obj->type == _DT_VAR) { - pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PN(obj->data.variable->pathing), 0, 0); - if(pathed && pathed->data) - obj = (_object_t*)pathed->data; - } -#endif /* MB_ENABLE_CLASS */ - if(!obj || obj->type != _DT_ROUTINE) { - _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ret.type = MB_DT_ROUTINE; - ret.value.routine = obj->data.routine; - ast = ast->next; - *l = ast; - - mb_check(mb_attempt_close_bracket(s, l)); - - mb_check(mb_push_value(s, l, ret)); - - ast = (_ls_node_t*)*l; - } - - break; - case _DT_VAR: - if(obj->data.variable->data->type == _DT_ROUTINE) { - obj = obj->data.variable->data; - - goto _retry; - } -#ifdef MB_ENABLE_CLASS - pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PN(obj->data.variable->pathing), 0, 0); - if(pathed && pathed->data) - obj = (_object_t*)pathed->data; - /* Fall through */ -#else /* MB_ENABLE_CLASS */ - mb_unrefvar(pathed); -#endif /* MB_ENABLE_CLASS */ - case _DT_ROUTINE: - obj = _GET_ROUTINE(obj); - routine = obj->data.routine; -#ifdef MB_ENABLE_CLASS -# ifdef MB_ENABLE_LAMBDA - if(routine->type != _IT_LAMBDA) { -# else /* MB_ENABLE_LAMBDA */ - { -# endif /* MB_ENABLE_LAMBDA */ - bool_t is_a0 = false; - bool_t is_a1 = false; - if(s->last_instance && routine->instance) { - _traverse_class(s->last_instance->created_from, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, routine->instance->created_from, &is_a0); - _traverse_class(routine->instance->created_from, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, s->last_instance->created_from, &is_a1); - } - if(routine->instance && ( - !s->last_instance || ( - s->last_instance && - !is_a0 && !is_a1 && - s->last_instance->created_from != routine->instance && - routine->instance->created_from != s->last_instance - ) - ) - ) { - pathed = _search_identifier_in_class(s, routine->instance, routine->name, 0, 0); - } else { - pathed = _search_identifier_in_scope_chain(s, 0, routine->name, _PATHING_NONE, 0, 0); - } - if(pathed && pathed->data) { - obj = (_object_t*)pathed->data; - obj = _GET_ROUTINE(obj); - routine = obj->data.routine; - } - } -#endif /* MB_ENABLE_CLASS */ -#ifdef MB_ENABLE_CLASS - s->calling = true; -#endif /* MB_ENABLE_CLASS */ - result = _eval_routine(s, &ast, 0, 0, routine, _has_routine_lex_arg, _pop_routine_lex_arg); -#ifdef MB_ENABLE_CLASS - s->calling = false; -#endif /* MB_ENABLE_CLASS */ - if(ast) - ast = ast->prev; - - break; - default: /* Do nothing */ - break; - } - - *l = ast; - -_exit: - return result; -} - -/* DEF statement */ -static int _core_def(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - _object_t* obj = 0; - _var_t* var = 0; - _ls_node_t* rnode = 0; - _routine_t* routine = 0; - - mb_assert(s && l); - - running = s->running_context; - - ast = (_ls_node_t*)*l; - ast = ast->next; - - _using_jump_set_of_structured(s, ast, _exit, result); - - obj = (_object_t*)ast->data; - if(!_IS_ROUTINE(obj)) { - _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(obj->data.routine->func.basic.entry) { - _handle_error_on_obj(s, SE_RN_DUPLICATE_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - routine = (_routine_t*)((_object_t*)ast->data)->data.routine; - ast = ast->next; - obj = (_object_t*)ast->data; - if(!_IS_FUNC(obj, _core_open_bracket)) { - _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - obj = (_object_t*)ast->data; - while(!_IS_FUNC(obj, _core_close_bracket)) { - if(obj->type == _DT_VAR) { - var = obj->data.variable; - rnode = _search_identifier_in_scope_chain(s, routine->func.basic.scope, var->name, _PATHING_NONE, 0, 0); - if(rnode) - var = ((_object_t*)rnode->data)->data.variable; - if(!routine->func.basic.parameters) - routine->func.basic.parameters = _ls_create(); - _ls_pushback(routine->func.basic.parameters, var); - } else if(_IS_FUNC(obj, _core_args)) { - if(!routine->func.basic.parameters) - routine->func.basic.parameters = _ls_create(); - _ls_pushback(routine->func.basic.parameters, (void*)&_VAR_ARGS); - ast = ast->next; - obj = (_object_t*)ast->data; - - break; - } - - ast = ast->next; - obj = (_object_t*)ast->data; - } - ast = ast->next; - routine->func.basic.entry = ast; - - _skip_to(s, &ast, _core_enddef, _DT_INVALID); - - ast = ast->next; - -_exit: - *l = ast; - - return result; -} - -/* ENDDEF statement */ -static int _core_enddef(mb_interpreter_t* s, void** l) { - int result = MB_SUB_RETURN; - _ls_node_t* ast = 0; - _ls_node_t* sub_stack = 0; - - mb_assert(s && l); - - sub_stack = s->sub_stack; - - ast = (_ls_node_t*)_ls_popback(sub_stack); - if(!ast) { - _handle_error_on_obj(s, SE_RN_NO_RETURN_POINT, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - *l = ast; - -_exit: - return result; -} - -/* ... (variable argument list) statement */ -static int _core_args(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _ls_node_t* var_args = 0; - bool_t pushed = false; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - if(ast) ast = ast->next; - *l = ast; - - var_args = s->var_args; - if(var_args) { - _object_t* obj = (_object_t*)_ls_popfront(var_args); - if(obj) { - mb_value_t arg; - mb_make_nil(arg); - _internal_object_to_public_value(obj, &arg); - mb_check(mb_push_value(s, l, arg)); - _UNREF(obj); - pushed = true; - _destroy_object_capsule_only(obj, 0); - } - } - - if(!pushed) { - mb_value_t arg; - mb_make_nil(arg); - arg.type = MB_DT_UNKNOWN; - mb_check(mb_push_value(s, l, arg)); - } - - return result; -} - -#ifdef MB_ENABLE_CLASS -/* CLASS statement */ -static int _core_class(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _running_context_t* running = 0; - _object_t* obj = 0; - _class_t* instance = 0; - _class_t* inherit = 0; - _class_t* last_inst = 0; - - mb_assert(s && l); - - running = s->running_context; - - ast = (_ls_node_t*)*l; - ast = ast->next; - - _using_jump_set_of_structured(s, ast, _exit, result); - - obj = (_object_t*)ast->data; - obj = _GET_CLASS(obj); - if(!_IS_CLASS(obj)) { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - instance = obj->data.instance; - ast = ast->next; - obj = (_object_t*)ast->data; - - last_inst = s->last_instance; - s->last_instance = instance; - - /* Process meta prototype list */ - if(_IS_FUNC(obj, _core_open_bracket)) { - do { - ast = ast->next; - obj = (_object_t*)ast->data; - if(obj && obj->type == _DT_VAR) { - _ls_node_t* tmp =_search_identifier_in_scope_chain(s, _OUTTER_SCOPE(running), obj->data.variable->name, _PATHING_NONE, 0, 0); - if(tmp && tmp->data) - obj = (_object_t*)tmp->data; - } - obj = _GET_CLASS(obj); - if(!_IS_CLASS(obj)) { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - inherit = obj->data.instance; - _link_meta_class(s, instance, inherit); - ast = ast->next; - obj = (_object_t*)ast->data; - } while(_IS_CLASS(obj) || _IS_SEP(obj, ',')); - if(_IS_FUNC(obj, _core_close_bracket)) { - ast = ast->next; - } else { - _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - } - - *l = ast; - - /* Execute class body */ - running = _push_scope_by_class(s, instance->scope); - do { - result = _execute_statement(s, (_ls_node_t**)l, true); - if(result != MB_FUNC_OK && s->error_handler) { - if(result >= MB_EXTENDED_ABORT) - s->last_error = SE_EA_EXTENDED_ABORT; - _handle_error_now(s, s->last_error, s->last_error_file, result); - - goto _pop_exit; - } - ast = (_ls_node_t*)*l; - if(!ast) - break; - obj = (_object_t*)ast->data; - } while(ast && !_IS_FUNC(obj, _core_endclass)); - _pop_scope(s, false); - - /* Search for meta functions */ - if(_search_class_hash_and_compare_functions(s, instance) != MB_FUNC_OK) { - _handle_error_on_obj(s, SE_RN_HASH_AND_COMPARE_MUST_COME_TOGETHER, s->source_file, DON(ast), MB_FUNC_WARNING, _exit, result); - } - - /* Finished */ - if(ast) { - _skip_to(s, &ast, _core_endclass, _DT_NIL); - - ast = ast->next; - } - -_pop_exit: - if(result != MB_FUNC_OK) - _pop_scope(s, false); - -_exit: - *l = ast; - - s->last_instance = last_inst; - - return result; -} - -/* ENDCLASS statement */ -static int _core_endclass(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - _do_nothing(s, l, _exit, result); - -_exit: - return result; -} - -/* NEW statement */ -static int _core_new(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - _object_t obj; - _object_t tgt; - mb_value_t ret; - _class_t* instance = 0; - - mb_assert(s && l); - - mb_make_nil(ret); - - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_func_end(s, l)); - - _MAKE_NIL(&obj); - _MAKE_NIL(&tgt); - switch(arg.type) { - case MB_DT_STRING: - arg.value.string = mb_strupr(arg.value.string); - if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) - goto _default; - _ref(&instance->ref, instance); - /* Fall through */ - case MB_DT_CLASS: - _public_value_to_internal_object(&arg, &obj); - _clone_object(s, &obj, &tgt, false, true); - ret.type = MB_DT_CLASS; - ret.value.instance = tgt.data.instance; - - break; - default: -_default: - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - _assign_public_value(&ret, 0); - -_exit: - _assign_public_value(&arg, 0); - - return result; -} - -/* VAR statement */ -static int _core_var(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_IGNORE; - _ls_node_t* ast = 0; - mb_unrefvar(s); - - ast = (_ls_node_t*)*l; - ast = ast->next; - - if(!s->last_instance) { - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - } - -_exit: - *l = ast; - - return result; -} - -/* REFLECT statement */ -static int _core_reflect(mb_interpreter_t* s, void** l) { -#ifdef MB_ENABLE_COLLECTION_LIB - int result = MB_FUNC_OK; - mb_value_t arg; - _object_t obj; - mb_value_t ret; - _class_t* instance = 0; - _dict_t* coll = 0; - - mb_assert(s && l); - - mb_make_nil(ret); - - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_func_end(s, l)); - - _MAKE_NIL(&obj); - switch(arg.type) { - case MB_DT_STRING: - arg.value.string = mb_strupr(arg.value.string); - if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) - goto _default; - _ref(&instance->ref, instance); - /* Fall through */ - case MB_DT_CLASS: - _public_value_to_internal_object(&arg, &obj); - coll = _create_dict(s); - _traverse_class(obj.data.instance, _reflect_class_field, 0, _META_LIST_MAX_DEPTH, false, coll, 0); - ret.type = MB_DT_DICT; - ret.value.dict = coll; - - break; - default: -_default: - _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - -_exit: - _assign_public_value(&arg, 0); - - return result; -#else /* MB_ENABLE_COLLECTION_LIB */ - mb_unrefvar(s); - mb_unrefvar(l); - - return MB_FUNC_ERR; -#endif /* MB_ENABLE_COLLECTION_LIB */ -} -#endif /* MB_ENABLE_CLASS */ - -#ifdef MB_ENABLE_LAMBDA -/* LAMBDA statement */ -static int _core_lambda(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t ret; - _running_context_t* running = 0; - _routine_t* routine = 0; - _ls_node_t* ast = 0; - int brackets = 0; - _var_t* var = 0; - _object_t* obj = 0; - unsigned ul = 0; - bool_t popped = false; - - mb_assert(s && l); - - /* Create lambda struct */ - routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); - running = _init_lambda(s, routine); - - /* Parameter list */ - _mb_check_mark(mb_attempt_open_bracket(s, l), result, _error); - - while(mb_has_arg(s, l)) { -#ifdef MB_ENABLE_CLASS - bool_t pathing = false; -#endif /* MB_ENABLE_CLASS */ - void* v = 0; - - if(!routine->func.lambda.parameters) - routine->func.lambda.parameters = _ls_create(); - - ast = (_ls_node_t*)*l; - if(ast && _IS_FUNC(ast->data, _core_args)) { - _ls_pushback(routine->func.lambda.parameters, (void*)&_VAR_ARGS); - ast = ast->next; - *l = ast; - - break; - } - - _mb_check_mark(mb_get_var(s, l, &v), result, _error); - - if(!v || ((_object_t*)v)->type != _DT_VAR) { - _handle_error_on_obj(s, SE_RN_INVALID_LAMBDA, s->source_file, DON2(l), MB_FUNC_ERR, _error, result); - } - var = ((_object_t*)v)->data.variable; -#ifdef MB_ENABLE_CLASS - pathing = var->pathing; -#endif /* MB_ENABLE_CLASS */ - - /* Add lambda parameters */ - obj = 0; - var = _create_var(&obj, var->name, 0, true); -#ifdef MB_ENABLE_CLASS - var->pathing = pathing; -#endif /* MB_ENABLE_CLASS */ - ul = _ht_set_or_insert(routine->func.lambda.scope->var_dict, var->name, obj); - mb_assert(ul); - _ls_pushback(routine->func.lambda.parameters, var); - - ast = (_ls_node_t*)*l; - if(_IS_FUNC(ast->data, _core_close_bracket)) - break; - *l = ast; - } - - _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); - - /* Lambda body */ - ast = (_ls_node_t*)*l; - if(ast) ast = ast->prev; - while(ast && _IS_EOS(ast->next->data)) - ast = ast->next; - *l = ast; - - _mb_check_mark(mb_attempt_open_bracket(s, l), result, _error); - - ast = (_ls_node_t*)*l; - routine->func.lambda.entry = ast; - while(ast && (brackets || !_IS_FUNC(ast->data, _core_close_bracket))) { - if(_IS_FUNC(ast->data, _core_open_bracket)) - brackets++; - else if(_IS_FUNC(ast->data, _core_close_bracket)) - brackets--; - - if(ast && !_is_valid_lambda_body_node(s, &routine->func.lambda, (_object_t*)ast->data)) { - _handle_error_on_obj(s, SE_RN_INVALID_LAMBDA, s->source_file, DON2(l), MB_FUNC_ERR, _error, result); - } - - /* Mark upvalues */ - if(ast) - _try_mark_upvalue(s, routine, (_object_t*)ast->data); - - ast = ast->next; - } - *l = ast; - routine->func.lambda.end = ast; - - _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); - - _pop_scope(s, false); - popped = true; - - /* Push the return value */ - ret.type = MB_DT_ROUTINE; - ret.value.routine = routine; - - _mb_check_mark(mb_push_value(s, l, ret), result, _error); - - /* Error processing */ - while(0) { -_error: - if(!popped) - _pop_scope(s, false); - if(routine) - _destroy_routine(s, routine); - } - - return result; -} -#endif /* MB_ENABLE_LAMBDA */ - -#ifdef MB_ENABLE_ALLOC_STAT -/* MEM statement */ -static int _core_mem(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)); - - mb_check(mb_push_int(s, l, (int_t)_mb_allocated)); - - return result; -} -#endif /* MB_ENABLE_ALLOC_STAT */ - -/* TYPE statement */ -static int _core_type(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - int i = 0; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - if(arg.type == MB_DT_STRING) { - mb_data_e types[] = { - MB_DT_NIL, - MB_DT_UNKNOWN, - MB_DT_INT, - MB_DT_REAL, - MB_DT_NUM, - MB_DT_STRING, - MB_DT_TYPE, - MB_DT_USERTYPE, -#ifdef MB_ENABLE_USERTYPE_REF - MB_DT_USERTYPE_REF, -#endif /* MB_ENABLE_USERTYPE_REF */ - MB_DT_ARRAY, -#ifdef MB_ENABLE_COLLECTION_LIB - MB_DT_LIST, - MB_DT_LIST_IT, - MB_DT_DICT, - MB_DT_DICT_IT, - MB_DT_COLLECTION, - MB_DT_ITERATOR, -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - MB_DT_CLASS, -#endif /* MB_ENABLE_CLASS */ - MB_DT_ROUTINE - }; - for(i = 0; i < countof(types); i++) { - unsigned e = types[i]; - if(!mb_stricmp(mb_get_type_string((mb_data_e)e), arg.value.string)) { - arg.value.type = (mb_data_e)e; - arg.type = MB_DT_TYPE; - - goto _found; - } - } - } - os = _try_overridden(s, l, &arg, _CORE_ID_TYPE, MB_MF_FUNC); - if((os & MB_MS_DONE) == MB_MS_NONE) { - arg.value.type = arg.type; - arg.type = MB_DT_TYPE; - } - -_found: - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, arg)); - } - - return result; -} - -/* IMPORT statement */ -static int _core_import(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)); - - return result; -} - -/* END statement */ -static int _core_end(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - - mb_assert(s && l); - - result = MB_FUNC_END; - - return result; -} - -/** Standard lib */ - -/* Get the absolute value of a number */ -static int _std_abs(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: - arg.value.integer = (int_t)abs(arg.value.integer); - - break; - case MB_DT_REAL: - arg.value.float_point = (real_t)fabs(arg.value.float_point); - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the sign of a number */ -static int _std_sgn(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: - arg.value.integer = sgn(arg.value.integer); - - break; - case MB_DT_REAL: - arg.value.integer = sgn(arg.value.float_point); - arg.type = MB_DT_INT; - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - -_exit: - return result; -} - -/* Get the square root of a number */ -static int _std_sqr(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, sqrt, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the greatest integer not greater than a number */ -static int _std_floor(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: /* Do nothing */ - break; - case MB_DT_REAL: - arg.value.integer = (int_t)floor(arg.value.float_point); - arg.type = MB_DT_INT; - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - -_exit: - return result; -} - -/* Get the least integer not less than a number */ -static int _std_ceil(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: /* Do nothing */ - break; - case MB_DT_REAL: - arg.value.integer = (int_t)ceil(arg.value.float_point); - arg.type = MB_DT_INT; - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - -_exit: - return result; -} - -/* Get the integer format of a number */ -static int _std_fix(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: /* Do nothing */ - break; - case MB_DT_REAL: - arg.value.integer = (int_t)(arg.value.float_point); - arg.type = MB_DT_INT; - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - -_exit: - return result; -} - -/* Get the rounded integer of a number */ -static int _std_round(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: /* Do nothing */ - break; - case MB_DT_REAL: - arg.value.integer = (int_t)(arg.value.float_point + (real_t)0.5f); - arg.type = MB_DT_INT; - - break; - default: - _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - - break; - } - mb_check(mb_push_int(s, l, arg.value.integer)); - -_exit: - return result; -} - -/* Set a random seed */ -static int _std_srnd(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - int_t seed = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_int(s, l, &seed)); - - mb_check(mb_attempt_close_bracket(s, l)); - - srand((unsigned)seed); - - return result; -} - -/* Get a random value among 0 ~ 1 or among given bounds */ -static int _std_rnd(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - real_t rnd = (real_t)0.0f; - - mb_assert(s && l); - - ast = (_ls_node_t*)*l; - - if(ast && ast->next && _IS_FUNC(ast->next->data, _core_open_bracket)) { - int_t lw = 0; - int_t hg = 0; - - mb_check(mb_attempt_open_bracket(s, l)); - - if(mb_has_arg(s, l)) { - mb_check(mb_pop_int(s, l, &hg)); - } - if(mb_has_arg(s, l)) { - lw = hg; - mb_check(mb_pop_int(s, l, &hg)); - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if(lw >= hg) { - _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - rnd = (real_t)rand() / RAND_MAX * (hg - lw + (real_t)0.99999f) + lw; /* [LOW, HIGH] */ - - mb_check(mb_push_int(s, l, (int_t)rnd)); - } else { - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_attempt_func_end(s, l)); - - rnd = (real_t)(((real_t)(rand() % 101)) / 100.0f); /* [0.0, 1.0] */ - - mb_check(mb_push_real(s, l, rnd)); - } - -_exit: - return result; -} - -/* Get the sin value of a number */ -static int _std_sin(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, sin, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the cos value of a number */ -static int _std_cos(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, cos, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the tan value of a number */ -static int _std_tan(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, tan, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the asin value of a number */ -static int _std_asin(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, asin, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the acos value of a number */ -static int _std_acos(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, acos, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the atan value of a number */ -static int _std_atan(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, atan, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the exp value of a number */ -static int _std_exp(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, exp, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the log value of a number */ -static int _std_log(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _math_calculate_fun_real(s, l, arg, log, _exit, result); - - mb_check(mb_push_value(s, l, arg)); - -_exit: - return result; -} - -/* Get the ASCII code of a character */ -static int _std_asc(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - char* arg = 0; - int_t val = 0; -#ifdef MB_ENABLE_UNICODE - size_t sz = 0; -#endif /* MB_ENABLE_UNICODE */ - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_string(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - if(arg[0] == _ZERO_CHAR) { - result = MB_FUNC_ERR; - - goto _exit; - } -#ifdef MB_ENABLE_UNICODE - sz = (size_t)mb_uu_ischar(arg); - if(sizeof(int_t) < sz) { - sz = sizeof(int_t); - _handle_error_on_obj(s, SE_RN_NUMBER_OVERFLOW, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - } - memcpy(&val, arg, sz); -#else /* MB_ENABLE_UNICODE */ - val = (int_t)arg[0]; -#endif /* MB_ENABLE_UNICODE */ - mb_check(mb_push_int(s, l, val)); - -_exit: - return result; -} - -/* Get the character of an ASCII code */ -static int _std_chr(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - int_t arg = 0; - char* chr = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_int(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - chr = (char*)mb_malloc(sizeof(arg) + 1); - memset(chr, 0, sizeof(arg) + 1); - memcpy(chr, &arg, sizeof(arg)); - mb_check(mb_push_string(s, l, chr)); - - return result; -} - -/* Get a number of characters from the left of a string */ -static int _std_left(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - char* arg = 0; - int_t count = 0; - char* sub = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_string(s, l, &arg)); - mb_check(mb_pop_int(s, l, &count)); - - mb_check(mb_attempt_close_bracket(s, l)); - - if(count <= 0) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -#ifdef MB_ENABLE_UNICODE - switch(mb_uu_substr(arg, 0, (int)count, &sub)) { - case 0: - _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - case -1: - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_UNICODE */ - sub = (char*)mb_malloc(count + 1); - memcpy(sub, arg, count); - sub[count] = _ZERO_CHAR; -#endif /* MB_ENABLE_UNICODE */ - mb_check(mb_push_string(s, l, sub)); - -_exit: - return result; -} - -/* Get a number of characters from a specific position of a string */ -static int _std_mid(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - char* arg = 0; - int_t start = 0; - int_t count = 0; - char* sub = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_string(s, l, &arg)); - mb_check(mb_pop_int(s, l, &start)); - mb_check(mb_pop_int(s, l, &count)); - - mb_check(mb_attempt_close_bracket(s, l)); - - if(count <= 0 || start < 0 || start >= (int_t)strlen(arg)) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -#ifdef MB_ENABLE_UNICODE - switch(mb_uu_substr(arg, start, (int)count, &sub)) { - case 0: - _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - case -1: - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_UNICODE */ - sub = (char*)mb_malloc(count + 1); - memcpy(sub, arg + start, count); - sub[count] = _ZERO_CHAR; -#endif /* MB_ENABLE_UNICODE */ - mb_check(mb_push_string(s, l, sub)); - -_exit: - return result; -} - -/* Get a number of characters from the right of a string */ -static int _std_right(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - char* arg = 0; - int_t count = 0; - char* sub = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_string(s, l, &arg)); - mb_check(mb_pop_int(s, l, &count)); - - mb_check(mb_attempt_close_bracket(s, l)); - - if(count <= 0) { - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - -#ifdef MB_ENABLE_UNICODE - switch(mb_uu_substr(arg, (int)(mb_uu_strlen(arg) - count), (int)count, &sub)) { - case 0: - _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - case -1: - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } -#else /* MB_ENABLE_UNICODE */ - sub = (char*)mb_malloc(count + 1); - memcpy(sub, arg + (strlen(arg) - count), count); - sub[count] = _ZERO_CHAR; -#endif /* MB_ENABLE_UNICODE */ - mb_check(mb_push_string(s, l, sub)); - -_exit: - return result; -} - -/* Get the string format of a number */ -static int _std_str(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - _dynamic_buffer_t buf; - size_t lbuf = 32; - - mb_assert(s && l); - - mb_make_nil(arg); - - _INIT_BUF(buf); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - - mb_check(mb_attempt_close_bracket(s, l)); - - switch(arg.type) { - case MB_DT_INT: - lbuf = 32; /* Enough for even 64bit integer */ - _RESIZE_CHAR_BUF(buf, lbuf); - if((size_t)sprintf(_CHAR_BUF_PTR(buf), MB_INT_FMT, arg.value.integer) >= lbuf) { - mb_assert(0 && "Buffer overflow."); - } - - break; - case MB_DT_REAL: - lbuf = 1 /* - */ + (DBL_MAX_10_EXP + 1) /* 308 + 1 digits */ + 1 /* . */ + 6 /* precision */ + 1 /* \0 */; - _RESIZE_CHAR_BUF(buf, lbuf); - if((size_t)sprintf(_CHAR_BUF_PTR(buf), MB_REAL_FMT, arg.value.float_point) >= lbuf) { - mb_assert(0 && "Buffer overflow."); - } - - break; -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: { - bool_t got_tostr = false; - _class_t* instance = (_class_t*)arg.value.instance; - _object_t val_obj; - _MAKE_NIL(&val_obj); - if((result = _format_class_to_string(s, l, instance, &val_obj, &got_tostr)) == MB_FUNC_OK) { - if(got_tostr) { - mb_check(mb_push_string(s, l, val_obj.data.string)); - } else { - const char* sp = mb_get_type_string(_internal_type_to_public_type(_DT_CLASS)); - char* ret = mb_strdup(sp, strlen(sp) + 1); - mb_check(mb_push_string(s, l, ret)); - } - - goto _exit; - } else { - goto _exit; - } - } -#endif /* MB_ENABLE_CLASS */ - default: - result = MB_FUNC_ERR; - - goto _exit; - } - mb_check(mb_push_string(s, l, _HEAP_CHAR_BUF(buf))); - -_exit: - return result; -} - -/* Get the number format of a string, or get the value of a dictionary iterator */ -static int _std_val(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - char* conv_suc = 0; - mb_value_t arg; -#ifdef MB_ENABLE_COLLECTION_LIB - _object_t ocoi; -#endif /* MB_ENABLE_COLLECTION_LIB */ - mb_value_t ret; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(arg); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &arg)); - os = _try_overridden(s, l, &arg, _STD_ID_VAL, MB_MF_FUNC); - if((os & MB_MS_DONE) == MB_MS_NONE) { - switch(arg.type) { - case MB_DT_STRING: - ret.value.integer = (int_t)mb_strtol(arg.value.string, &conv_suc, 0); - if(*conv_suc == _ZERO_CHAR) { - ret.type = MB_DT_INT; - mb_check(mb_push_value(s, l, ret)); - - goto _exit; - } - ret.value.float_point = (real_t)mb_strtod(arg.value.string, &conv_suc); - if(*conv_suc == _ZERO_CHAR) { - ret.type = MB_DT_REAL; - mb_check(mb_push_value(s, l, ret)); - - goto _exit; - } - result = MB_FUNC_ERR; - - break; -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_DICT_IT: - _MAKE_NIL(&ocoi); - _public_value_to_internal_object(&arg, &ocoi); - if(ocoi.data.dict_it && ocoi.data.dict_it->curr_node && ocoi.data.dict_it->curr_node->data) { - _internal_object_to_public_value((_object_t*)ocoi.data.dict_it->curr_node->data, &ret); - mb_check(mb_push_value(s, l, ret)); - } else { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ - default: - _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, ret)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - return result; -} - -/* Get the length of a string or an array, or element count of a collection or a variable argument list */ -static int _std_len(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - mb_value_t arg; - _array_t* arr = 0; -#ifdef MB_ENABLE_COLLECTION_LIB - _list_t* lst = 0; - _dict_t* dct = 0; -#endif /* MB_ENABLE_COLLECTION_LIB */ - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - ast = (_ls_node_t*)*l; - if(ast) obj = (_object_t*)ast->data; - if(obj && _IS_FUNC(obj, _core_args)) { - ast = ast->next; - *l = ast; - mb_check(mb_push_int(s, l, s->var_args ? (int_t)_ls_count(s->var_args) : 0)); - - mb_check(mb_attempt_close_bracket(s, l)); - - goto _exit; - } - mb_check(mb_pop_value(s, l, &arg)); - os = _try_overridden(s, l, &arg, _STD_ID_LEN, MB_MF_FUNC); - if((os & MB_MS_DONE) == MB_MS_NONE) { - switch(arg.type) { - case MB_DT_STRING: -#ifdef MB_ENABLE_UNICODE - mb_check(mb_push_int(s, l, (int_t)mb_uu_strlen(arg.value.string))); -#else /* MB_ENABLE_UNICODE */ - mb_check(mb_push_int(s, l, (int_t)strlen(arg.value.string))); -#endif /* MB_ENABLE_UNICODE */ - - break; - case MB_DT_ARRAY: - arr = (_array_t*)arg.value.array; - mb_check(mb_push_int(s, l, (int_t)arr->count)); - - break; -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - lst = (_list_t*)arg.value.list; - mb_check(mb_push_int(s, l, (int_t)lst->count)); - _assign_public_value(&arg, 0); - - break; - case MB_DT_DICT: - dct = (_dict_t*)arg.value.dict; - mb_check(mb_push_int(s, l, (int_t)_ht_count(dct->dict))); - _assign_public_value(&arg, 0); - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ - default: - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - - break; - } - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_int(s, l, 0)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - return result; -} - -/* GET statement */ -static int _std_get(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t ov; - mb_value_t arg; - _object_t obj; -#ifdef MB_ENABLE_COLLECTION_LIB - int_t index = 0; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - char* field = 0; - _ls_node_t* fnode = 0; - _object_t* fobj = 0; -#endif /* MB_ENABLE_CLASS */ - mb_value_t ret; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(ov); - mb_make_nil(arg); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &ov)); - os = _try_overridden(s, l, &ov, _STD_ID_GET, MB_MF_FUNC); - if((os & MB_MS_DONE) == MB_MS_NONE) { - _MAKE_NIL(&obj); - switch(ov.type) { -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - _public_value_to_internal_object(&ov, &obj); - mb_check(mb_pop_int(s, l, &index)); - if(!_at_list(obj.data.list, index, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&ov, &obj); - mb_check(mb_pop_value(s, l, &arg)); - if(!_find_dict(obj.data.dict, &arg, &ret)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_LIST_IT: - _public_value_to_internal_object(&ov, &obj); - if(obj.data.list_it && !obj.data.list_it->list->range_begin && obj.data.list_it->curr.node && obj.data.list_it->curr.node->data) { - _internal_object_to_public_value((_object_t*)obj.data.list_it->curr.node->data, &ret); - } else if(obj.data.list_it && obj.data.list_it->list->range_begin) { - mb_make_int(ret, obj.data.list_it->curr.ranging); - } else { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; - case MB_DT_DICT_IT: - _public_value_to_internal_object(&ov, &obj); - if(obj.data.dict_it && obj.data.dict_it->curr_node && obj.data.dict_it->curr_node->extra) { - _internal_object_to_public_value((_object_t*)obj.data.dict_it->curr_node->extra, &ret); - } else { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: - _public_value_to_internal_object(&ov, &obj); - mb_check(mb_pop_string(s, l, &field)); - field = mb_strupr(field); - fnode = _search_identifier_in_class(s, obj.data.instance, field, 0, 0); - if(fnode && fnode->data) { - fobj = (_object_t*)fnode->data; - _internal_object_to_public_value(fobj, &ret); - } - - break; -#endif /* MB_ENABLE_CLASS */ - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_OR_ITERATOR_OR_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, ret)); - } - -_exit: - _assign_public_value(&ov, 0); - - return result; -} - -/* SET statement */ -static int _std_set(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t ov; - mb_value_t key; - mb_value_t val; - _object_t obj; -#ifdef MB_ENABLE_COLLECTION_LIB - _object_t* oval = 0; - int_t idx = 0; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - char* field = 0; - _ls_node_t* fnode = 0; - _object_t* fobj = 0; - mb_value_t nv; -#endif /* MB_ENABLE_CLASS */ - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(ov); - mb_make_nil(key); - mb_make_nil(val); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &ov)); - os = _try_overridden(s, l, &ov, _STD_ID_SET, MB_MF_FUNC); - if((os & MB_MS_DONE) == MB_MS_NONE) { - _MAKE_NIL(&obj); - switch(ov.type) { -#ifdef MB_ENABLE_COLLECTION_LIB - case MB_DT_LIST: - _public_value_to_internal_object(&ov, &obj); - while(mb_has_arg(s, l)) { - mb_make_nil(val); - mb_check(mb_pop_int(s, l, &idx)); - mb_check(mb_pop_value(s, l, &val)); - if(!_set_list(obj.data.list, idx, &val, &oval)) { - _destroy_object(oval, 0); - - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&ov, &obj); - while(mb_has_arg(s, l)) { - mb_make_nil(key); - mb_make_nil(val); - mb_check(mb_pop_value(s, l, &key)); - mb_check(mb_pop_value(s, l, &val)); - _set_dict(obj.data.dict, &key, &val, 0, 0); - } - - break; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - case MB_DT_CLASS: - mb_make_nil(nv); - - _public_value_to_internal_object(&ov, &obj); - mb_check(mb_pop_string(s, l, &field)); - mb_check(mb_pop_value(s, l, &nv)); - field = mb_strupr(field); - fnode = _search_identifier_in_class(s, obj.data.instance, field, 0, 0); - if(fnode && _IS_VAR(fnode->data)) { - _object_t* nobj = 0; - fobj = (_object_t*)fnode->data; - _destroy_object(fobj->data.variable->data, 0); - _create_internal_object_from_public_value(&nv, &nobj); - fobj->data.variable->data = nobj; - } else { - _handle_error_on_obj(s, SE_RN_VARIABLE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - break; -#endif /* MB_ENABLE_CLASS */ - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, ov)); - } - -_exit: - _assign_public_value(&ov, 0); - - return result; -} - -/* PRINT statement */ -static int _std_print(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - _object_t val_obj; - _object_t* val_ptr = 0; - bool_t pathed_str = false; - - mb_assert(s && l); - - ++s->no_eat_comma_mark; - ast = (_ls_node_t*)*l; - ast = ast->next; - if(!ast || !ast->data) { - _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - - obj = (_object_t*)ast->data; - do { - pathed_str = false; - val_ptr = &val_obj; - _MAKE_NIL(val_ptr); - switch(obj->type) { - case _DT_VAR: - if(obj->data.variable->data->type == _DT_ROUTINE) { - obj = obj->data.variable->data; - val_ptr = _eval_var_in_print(s, &val_ptr, &ast, obj); - - goto _print; - } -#ifdef MB_ENABLE_CLASS - if(obj->data.variable->pathing) { - _ls_node_t* pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PU(obj->data.variable->pathing), 0, 0); - if(pathed && pathed->data) { - if(obj != (_object_t*)pathed->data) { - obj = (_object_t*)pathed->data; - val_ptr = _eval_var_in_print(s, &val_ptr, &ast, obj); - if(val_ptr->type == _DT_STRING) - pathed_str = true; - } - } - - goto _print; - } -#endif /* MB_ENABLE_CLASS */ - /* Fall through */ - case _DT_ARRAY: /* Fall through */ - case _DT_NIL: /* Fall through */ - case _DT_INT: /* Fall through */ - case _DT_REAL: /* Fall through */ - case _DT_STRING: /* Fall through */ - case _DT_TYPE: /* Fall through */ -#ifdef MB_ENABLE_CLASS - case _DT_CLASS: /* Fall through */ -#endif /* MB_ENABLE_CLASS */ - case _DT_FUNC: /* Fall through */ - case _DT_ROUTINE: - result = _calc_expression(s, &ast, &val_ptr); - _REF(val_ptr); - _UNREF(val_ptr); -_print: - if(val_ptr->type == _DT_NIL) { - _get_printer(s)(MB_NIL); - } else if(val_ptr->type == _DT_INT) { - _get_printer(s)(MB_INT_FMT, val_ptr->data.integer); - } else if(val_ptr->type == _DT_REAL) { - _get_printer(s)(MB_REAL_FMT, val_ptr->data.float_point); - } else if(val_ptr->type == _DT_STRING) { - _print_string(s, val_ptr); - if(!val_ptr->ref && val_ptr->data.string && !pathed_str) { - safe_free(val_ptr->data.string); - } -#ifdef MB_ENABLE_USERTYPE_REF - } else if(val_ptr->type == _DT_USERTYPE_REF) { - if(val_ptr->data.usertype_ref->fmt) { - _dynamic_buffer_t buf; - size_t lbuf = 0; - _INIT_BUF(buf); - while((lbuf = (size_t)val_ptr->data.usertype_ref->fmt(s, val_ptr->data.usertype_ref->usertype, _CHAR_BUF_PTR(buf), (unsigned)_CHARS_OF_BUF(buf))) > _CHARS_OF_BUF(buf)) { - _RESIZE_CHAR_BUF(buf, lbuf); - } - _get_printer(s)("%s", _CHAR_BUF_PTR(buf)); - _DISPOSE_BUF(buf); - } else { - _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(val_ptr->type))); - } - _unref(&val_ptr->data.usertype_ref->ref, val_ptr->data.usertype_ref); -#endif /* MB_ENABLE_USERTYPE_REF */ - } else if(val_ptr->type == _DT_TYPE) { - _get_printer(s)(mb_get_type_string(val_ptr->data.type)); -#ifdef MB_ENABLE_CLASS - } else if(val_ptr->type == _DT_CLASS) { - bool_t got_tostr = false; - _class_t* instance = val_ptr->data.instance; - val_ptr = &val_obj; - _MAKE_NIL(val_ptr); - if((result = _format_class_to_string(s, (void**)&ast, instance, val_ptr, &got_tostr)) == MB_FUNC_OK) { - if(got_tostr) { - obj = val_ptr; - - goto _print; - } else { - _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(_DT_CLASS))); - } - } else { - goto _exit; - } -#endif /* MB_ENABLE_CLASS */ - } else { - _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(val_ptr->type))); - } - if(result != MB_FUNC_OK) - goto _exit; - /* Fall through */ - case _DT_SEP: - if(!ast) - break; - obj = (_object_t*)ast->data; -#if _COMMA_AS_NEWLINE - if(obj->data.separator == ',') { -#else /* _COMMA_AS_NEWLINE */ - if(obj->data.separator == ';') { -#endif /* _COMMA_AS_NEWLINE */ - _get_printer(s)("\n"); - } - - break; - default: - _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - - break; - } - - if(!ast) - break; - obj = (_object_t*)ast->data; - if(_is_print_terminal(s, obj)) - break; - if(_IS_SEP(obj, ',') || _IS_SEP(obj, ';')) { - ast = ast->next; - obj = (_object_t*)ast->data; - } else { - _handle_error_on_obj(s, SE_RN_COMMA_OR_SEMICOLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - } while(ast && !_IS_SEP(obj, ':') && !_IS_FUNC(obj, _core_close_bracket) && (obj->type == _DT_SEP || !_is_expression_terminal(s, obj))); - -_exit: - --s->no_eat_comma_mark; - - *l = ast; - if(result != MB_FUNC_OK) - _get_printer(s)("\n"); - - return result; -} - -/* INPUT statement */ -static int _std_input(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - _ls_node_t* ast = 0; - _object_t* obj = 0; - char line[_INPUT_MAX_LENGTH]; - char* conv_suc = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_func_begin(s, l)); - - mb_check(mb_attempt_func_end(s, l)); - - ast = (_ls_node_t*)*l; - obj = (_object_t*)ast->data; - - if(!obj || obj->type == _DT_EOS) { -#ifdef MB_CP_VC - getch(); -#else /* MB_CP_VC */ - _get_inputer(s)(line, sizeof(line)); -#endif /* MB_CP_VC */ - - goto _exit; - } - if(obj->type == _DT_STRING) { - _print_string(s, obj); - ast = ast->next; - obj = (_object_t*)ast->data; - if(!_IS_SEP(obj, ',')) { - _handle_error_on_obj(s, SE_RN_COMMA_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - ast = ast->next; - obj = (_object_t*)ast->data; - } - if(obj->type != _DT_VAR) { - _handle_error_on_obj(s, SE_RN_VARIABLE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); - } - if(obj->data.variable->data->type == _DT_INT || obj->data.variable->data->type == _DT_REAL) { - _get_inputer(s)(line, sizeof(line)); - obj->data.variable->data->type = _DT_INT; - obj->data.variable->data->data.integer = (int_t)mb_strtol(line, &conv_suc, 0); - if(*conv_suc != _ZERO_CHAR) { - obj->data.variable->data->type = _DT_REAL; - obj->data.variable->data->data.float_point = (real_t)mb_strtod(line, &conv_suc); - if(*conv_suc != _ZERO_CHAR) { - result = MB_FUNC_ERR; - - goto _exit; - } - } - ast = ast->next; - } else if(obj->data.variable->data->type == _DT_STRING) { - size_t len = 0; - if(obj->data.variable->data->data.string) { - safe_free(obj->data.variable->data->data.string); - } - len = (size_t)_get_inputer(s)(line, sizeof(line)); -#if defined MB_CP_VC && defined MB_ENABLE_UNICODE - do { - _dynamic_buffer_t buf; - _dynamic_buffer_t wbuf; - _INIT_BUF(buf); - _INIT_BUF(wbuf); - while((len = (size_t)mb_bytes_to_wchar_ansi(line, &_WCHAR_BUF_PTR(wbuf), _WCHARS_OF_BUF(wbuf))) > _WCHARS_OF_BUF(wbuf)) { - _RESIZE_WCHAR_BUF(wbuf, len); - } - while((len = mb_wchar_to_bytes(_WCHAR_BUF_PTR(wbuf), &_CHAR_BUF_PTR(buf), _CHARS_OF_BUF(buf))) > _CHARS_OF_BUF(buf)) { - _RESIZE_CHAR_BUF(buf, len); - } - _DISPOSE_BUF(wbuf); - obj->data.variable->data->data.string = _HEAP_CHAR_BUF(buf); - } while(0); -#else /* MB_CP_VC && MB_ENABLE_UNICODE */ - obj->data.variable->data->data.string = mb_memdup(line, (unsigned)(len + 1)); -#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ - ast = ast->next; - } else { - result = MB_FUNC_ERR; - - goto _exit; - } - -_exit: - *l = ast; - - return result; -} - -/** Collection lib */ - -#ifdef MB_ENABLE_COLLECTION_LIB -/* LIST statement */ -static int _coll_list(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - _list_t* coll = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - coll = _create_list(s); - - if(mb_has_arg(s, l)) { - _ls_node_t* ast = 0; - _object_t* obj = 0; - mb_make_nil(arg); - _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); - ast = (_ls_node_t*)*l; - if(ast) obj = (_object_t*)ast->data; - if(arg.type == MB_DT_INT && obj && _IS_FUNC(obj, _core_to)) { - /* Push a range of integer */ - int_t begin = arg.value.integer; - int_t end = 0; - int_t step = 0; - ast = ast->next; - _mb_check_mark(mb_pop_int(s, (void**)&ast, &end), result, _error); - step = sgn(end - begin); - coll->range_begin = (int_t*)mb_malloc(sizeof(int_t)); - *coll->range_begin = begin; - coll->count = end - begin + step; - if(!coll->count) coll->count = 1; - *l = ast; - } else { - /* Push arguments */ - _push_list(coll, &arg, 0); - while(mb_has_arg(s, l)) { - mb_make_nil(arg); - _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); - _push_list(coll, &arg, 0); - } - } - } - - _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); - - arg.type = MB_DT_LIST; - arg.value.list = coll; - _mb_check_mark(mb_push_value(s, l, arg), result, _error); - - while(0) { -_error: - mb_make_nil(arg); - mb_push_value(s, l, arg); - _destroy_list(coll); - } - - return result; -} - -/* DICT statement */ -static int _coll_dict(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t arg; - mb_value_t val; - _dict_t* coll = 0; - - mb_assert(s && l); - - mb_check(mb_attempt_open_bracket(s, l)); - - coll = _create_dict(s); - - while(mb_has_arg(s, l)) { - mb_make_nil(arg); - mb_make_nil(val); - _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); - _mb_check_mark(mb_pop_value(s, l, &val), result, _error); - _set_dict(coll, &arg, &val, 0, 0); - } - - _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); - - arg.type = MB_DT_DICT; - arg.value.dict = coll; - _mb_check_mark(mb_push_value(s, l, arg), result, _error); - - while(0) { -_error: - mb_make_nil(arg); - mb_push_value(s, l, arg); - _destroy_dict(coll); - } - - return result; -} - -/* PUSH statement */ -static int _coll_push(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - mb_value_t arg; - _object_t olst; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_PUSH, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - if(coll.type != MB_DT_LIST) { - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _MAKE_NIL(&olst); - _public_value_to_internal_object(&coll, &olst); - - while(mb_has_arg(s, l)) { - mb_make_nil(arg); - mb_check(mb_pop_value(s, l, &arg)); - _push_list(olst.data.list, &arg, 0); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* POP statement */ -static int _coll_pop(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - mb_value_t val; - _object_t olst; - _object_t ocoll; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(val); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_POP, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - if(coll.type != MB_DT_LIST) { - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - _MAKE_NIL(&olst); - _public_value_to_internal_object(&coll, &olst); - if(_pop_list(olst.data.list, &val, s)) { - mb_check(mb_push_value(s, l, val)); - _MAKE_NIL(&ocoll); - _public_value_to_internal_object(&val, &ocoll); - _UNREF(&ocoll) - - _assign_public_value(&coll, 0); - } else { - mb_check(mb_push_value(s, l, val)); - - _assign_public_value(&coll, 0); - - _handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - } - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, val)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - return result; -} - -/* PEEK statement */ -static int _coll_peek(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - mb_value_t val; - _object_t olst; - _object_t* oval = 0; - _ls_node_t* node = 0; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(val); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_PEEK, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - if(coll.type != MB_DT_LIST) { - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - _MAKE_NIL(&olst); - _public_value_to_internal_object(&coll, &olst); - node = _ls_back(olst.data.list->list); - oval = node ? (_object_t*)node->data : 0; - if(oval) { - _internal_object_to_public_value(oval, &val); - - mb_check(mb_push_value(s, l, val)); - - _assign_public_value(&coll, 0); - } else { - mb_check(mb_push_value(s, l, val)); - - _assign_public_value(&coll, 0); - - _handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); - } - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, val)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - return result; -} - -/* INSERT statement */ -static int _coll_insert(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - int_t idx = 0; - mb_value_t arg; - _object_t olst; - _object_t* oval = 0; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(arg); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_INSERT, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - mb_check(mb_pop_int(s, l, &idx)); - mb_check(mb_pop_value(s, l, &arg)); - - if(coll.type != MB_DT_LIST) { - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _MAKE_NIL(&olst); - _public_value_to_internal_object(&coll, &olst); - - if(!_insert_list(olst.data.list, idx, &arg, &oval)) { - _destroy_object(oval, 0); - - _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - - mb_check(mb_push_value(s, l, coll)); - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* SORT statement */ -static int _coll_sort(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - _object_t olst; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_SORT, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - if(coll.type != MB_DT_LIST) { - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - _MAKE_NIL(&olst); - _public_value_to_internal_object(&coll, &olst); - - _sort_list(olst.data.list); - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* EXIST statement */ -static int _coll_exist(mb_interpreter_t* s, void** l){ - int result = MB_FUNC_OK; - mb_value_t coll; - mb_value_t arg; - _object_t ocoll; - mb_value_t ret; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(arg); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_EXIST, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - mb_check(mb_pop_value(s, l, &arg)); - - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - mb_make_bool(ret, _find_list(ocoll.data.list, &arg, 0)); - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - mb_make_bool(ret, _find_dict(ocoll.data.dict, &arg, 0)); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - mb_check(mb_push_value(s, l, ret)); - } else { - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* INDEX_OF statement */ -static int _coll_index_of(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - int idx = 0; - mb_value_t coll; - _object_t ocoll; - mb_value_t val; - mb_value_t ret; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(val); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_INDEX_OF, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - ret.type = MB_DT_UNKNOWN; - mb_check(mb_pop_value(s, l, &val)); - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - if(_find_list(ocoll.data.list, &val, &idx)) { - mb_make_int(ret, (int_t)idx); - } - - break; - default: - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, ret)); - } - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* REMOVE statement */ -static int _coll_remove(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - int_t idx = 0; - mb_value_t key; - _object_t ocoll; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(key); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_REMOVE, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - while(mb_has_arg(s, l)) { - mb_check(mb_pop_int(s, l, &idx)); - - if(!_remove_at_list(ocoll.data.list, idx)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - while(mb_has_arg(s, l)) { - mb_check(mb_pop_value(s, l, &key)); - - if(!_remove_dict(ocoll.data.dict, &key)) { - _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } - } - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* CLEAR statement */ -static int _coll_clear(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - _object_t ocoll; - mb_meta_status_u os = MB_MS_NONE; - - mb_assert(s && l); - - mb_make_nil(coll); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - os = _try_overridden(s, l, &coll, _COLL_ID_CLEAR, MB_MF_COLL); - if((os & MB_MS_DONE) == MB_MS_NONE) { - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - _clear_list(ocoll.data.list); - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - _clear_dict(ocoll.data.dict); - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - } - - mb_check(mb_attempt_close_bracket(s, l)); - - if((os & MB_MS_RETURNED) == MB_MS_NONE) { - mb_check(mb_push_value(s, l, coll)); - } - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* CLONE statement */ -static int _coll_clone(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - _object_t ocoll; - _object_t otgt; - mb_value_t ret; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _MAKE_NIL(&ocoll); - _MAKE_NIL(&otgt); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - _clone_object(s, &ocoll, &otgt, false, true); - ret.type = MB_DT_LIST; - ret.value.list = otgt.data.list; - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - _clone_object(s, &ocoll, &otgt, false, true); - ret.type = MB_DT_DICT; - ret.value.dict = otgt.data.dict; - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* TO_ARRAY statement */ -static int _coll_to_array(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - _object_t ocoll; - _array_t* array = 0; - _array_helper_t helper; - mb_value_t ret; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - array = _create_array(s, mb_strdup("", 1), _DT_REAL); - array->count = ocoll.data.list->count; - array->dimension_count = 1; - array->dimensions[0] = ocoll.data.list->count; - _init_array(array); - helper.s = s; - helper.array = array; - helper.index = 0; - _LS_FOREACH(ocoll.data.list->list, _do_nothing_on_object, _copy_list_to_array, &helper); - ret.type = MB_DT_ARRAY; - ret.value.array = array; - - break; - default: - _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* ITERATOR statement */ -static int _coll_iterator(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t coll; - _object_t ocoll; - _list_it_t* lit = 0; - _dict_it_t* dit = 0; - mb_value_t ret; - - mb_assert(s && l); - - mb_make_nil(coll); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &coll)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _MAKE_NIL(&ocoll); - switch(coll.type) { - case MB_DT_LIST: - _public_value_to_internal_object(&coll, &ocoll); - lit = _create_list_it(ocoll.data.list, false); - ret.type = MB_DT_LIST_IT; - ret.value.list_it = lit; - - break; - case MB_DT_DICT: - _public_value_to_internal_object(&coll, &ocoll); - dit = _create_dict_it(ocoll.data.dict, false); - ret.type = MB_DT_DICT_IT; - ret.value.list_it = dit; - - break; - default: - _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - -_exit: - _assign_public_value(&coll, 0); - - return result; -} - -/* MOVE_NEXT statement */ -static int _coll_move_next(mb_interpreter_t* s, void** l) { - int result = MB_FUNC_OK; - mb_value_t it; - _object_t oit; - mb_value_t ret; - - mb_assert(s && l); - - mb_make_nil(it); - mb_make_nil(ret); - - mb_check(mb_attempt_open_bracket(s, l)); - - mb_check(mb_pop_value(s, l, &it)); - - mb_check(mb_attempt_close_bracket(s, l)); - - _MAKE_NIL(&oit); - switch(it.type) { - case MB_DT_LIST_IT: - _public_value_to_internal_object(&it, &oit); - oit.data.list_it = _move_list_it_next(oit.data.list_it); - if(_invalid_list_it(oit.data.list_it)) { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } else if(oit.data.list_it) { - mb_make_bool(ret, true); - } else { - mb_make_nil(ret); - } - - break; - case MB_DT_DICT_IT: - _public_value_to_internal_object(&it, &oit); - oit.data.dict_it = _move_dict_it_next(oit.data.dict_it); - if(_invalid_dict_it(oit.data.dict_it)) { - _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - } else if(oit.data.dict_it) { - mb_make_bool(ret, true); - } else { - mb_make_nil(ret); - } - - break; - default: - _handle_error_on_obj(s, SE_RN_ITERATOR_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); - - break; - } - - mb_check(mb_push_value(s, l, ret)); - -_exit: - return result; -} -#endif /* MB_ENABLE_COLLECTION_LIB */ - -/* ========================================================} */ - -#ifdef MB_COMPACT_MODE -# pragma pack() -#endif /* MB_COMPACT_MODE */ - -#ifdef MB_CP_BORLANDC -# pragma warn .8004 -# pragma warn .8008 -# pragma warn .8012 -#endif /* MB_CP_BORLANDC */ - -#ifdef MB_CP_CLANG -# pragma clang diagnostic pop -#endif /* MB_CP_CLANG */ - -#ifdef MB_CP_VC -# pragma warning(pop) -#endif /* MB_CP_VC */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +/* +** This source file is part of MY-BASIC +** +** For the latest info, see https://github.com/paladin-t/my_basic/ +** +** Copyright (C) 2011 - 2016 Wang 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 "my_basic.h" +#if defined ARDUINO && !defined MB_CP_ARDUINO +# define MB_CP_ARDUINO +#endif /* ARDUINO && !MB_CP_ARDUINO */ +#ifdef MB_CP_VC +# include +# include +# include +# include +#else /* MB_CP_VC */ +# include +#endif /* MB_CP_VC */ +#ifndef MB_CP_ARDUINO +# include +#endif /* MB_CP_ARDUINO */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef MB_CP_VC +# pragma warning(push) +# pragma warning(disable : 4127) +# pragma warning(disable : 4305) +# pragma warning(disable : 4309) +# pragma warning(disable : 4805) +# pragma warning(disable : 4996) +#endif /* MB_CP_VC */ + +#ifdef MB_CP_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-variable" +#endif /* MB_CP_CLANG */ + +#ifdef MB_CP_BORLANDC +# pragma warn -8004 +# pragma warn -8008 +# pragma warn -8012 +#endif /* MB_CP_BORLANDC */ + +#ifdef MB_COMPACT_MODE +# pragma pack(1) +#endif /* MB_COMPACT_MODE */ + +/* +** {======================================================== +** Data type declarations +*/ + +/** Macros */ + +/* Version information */ +#define _VER_MAJOR 1 +#define _VER_MINOR 2 +#define _VER_REVISION 0 +#define _VER_SUFFIX +#define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) +#define _STRINGIZE(A) _MAKE_STRINGIZE(A) +#define _MAKE_STRINGIZE(A) #A +#if _VER_REVISION == 0 +# define _MB_VERSION_STRING _STRINGIZE(_VER_MAJOR._VER_MINOR _VER_SUFFIX) +#else /* _VER_REVISION == 0 */ +# define _MB_VERSION_STRING _STRINGIZE(_VER_MAJOR._VER_MINOR._VER_REVISION _VER_SUFFIX) +#endif /* _VER_REVISION == 0 */ + +/* Define as 1 to create hash table nodes lazily, 0 obligingly */ +#define _LAZY_HASH_TABLE 1 + +/* Define as 1 to treat warning as error, 0 just leave it */ +#define _WARNING_AS_ERROR 0 + +/* Define as 1 to use a comma to PRINT a new line, 0 to use a semicolon */ +#define _COMMA_AS_NEWLINE 0 + +#define _NO_EAT_COMMA 2 + +/* Hash table size */ +#define _HT_ARRAY_SIZE_TINY 1 +#define _HT_ARRAY_SIZE_SMALL 193 +#define _HT_ARRAY_SIZE_MID 1543 +#define _HT_ARRAY_SIZE_BIG 12289 +#define _HT_ARRAY_SIZE_DEFAULT _HT_ARRAY_SIZE_SMALL + +/* Max length of a single symbol */ +#define _SINGLE_SYMBOL_MAX_LENGTH 128 + +/* Buffer length of some string operations */ +#define _INPUT_MAX_LENGTH 256 +#define _TEMP_FORMAT_MAX_LENGTH 32 +#define _LAMBDA_NAME_MAX_LENGTH 32 + +/* Helper */ +#ifndef sgn +# define sgn(__v) ((__v) ? ((__v) > 0 ? 1 : -1) : 0) +#endif /* sgn */ + +#ifndef countof +# define countof(__a) (sizeof(__a) / sizeof(*(__a))) +#endif /* countof */ + +#ifndef islower +# define islower(__c) ((__c) >= 'a' && (__c) <= 'z') +#endif /* islower */ + +#ifndef toupper +# define toupper(__c) (islower(__c) ? ((__c) - 'a' + 'A') : (__c)) +#endif /* toupper */ + +#ifndef _mb_unaligned +# if defined MB_CP_VC && defined MB_OS_WIN64 +# define _mb_unaligned __unaligned +# else +# define _mb_unaligned +# endif +#endif /* _mb_unaligned */ + +#define _COPY_BYTES(__l, __r) do { memcpy((__l), (__r), sizeof(mb_val_bytes_t)); } while(0) + +#define _mb_check(__expr, __exit) do { if((__expr) != MB_FUNC_OK) goto __exit; } while(0) +#define _mb_check_mark(__expr, __result, __exit) do { __result = (__expr); if(__result != MB_FUNC_OK) goto __exit; } while(0) + +#define DON(__o) ((__o) ? ((_object_t*)((__o)->data)) : 0) +#define DON2(__a) (((__a) && *(__a)) ? (_object_t*)((*((_ls_node_t**)(__a)))->data) : 0) +#define TON(__t) (((__t) && *(__t)) ? (_object_t*)(((_tuple3_t*)(*(__t)))->e1) : 0) + +#define _IS_VAR_ARGS(__v) ((__v) == &_VAR_ARGS) + +#define _IS_EOS(__o) (__o && ((_object_t*)(__o))->type == _DT_EOS) +#define _IS_SEP(__o, __c) (((_object_t*)(__o))->type == _DT_SEP && ((_object_t*)(__o))->data.separator == __c) +#define _IS_FUNC(__o, __f) (((_object_t*)(__o))->type == _DT_FUNC && ((_object_t*)(__o))->data.func->pointer == __f) +#define _IS_UNARY_FUNC(__o) (((_object_t*)(__o))->type == _DT_FUNC && _is_unary(((_object_t*)(__o))->data.func->pointer)) +#define _IS_VAR(__o) ((__o) && ((_object_t*)(__o))->type == _DT_VAR) + +/* Collection functors */ +#define _OP_RESULT_NORMAL 0 +#define _OP_RESULT_DEL_NODE -1 + +typedef int (* _common_compare)(void*, void*); +typedef int (* _common_operation)(void*, void*); + +/** List */ + +typedef _common_compare _ls_compare; +typedef _common_operation _ls_operation; + +typedef struct _ls_node_t { + void* data; + struct _ls_node_t* prev; + struct _ls_node_t* next; + void* extra; +} _ls_node_t; + +/** Dictionary */ + +typedef unsigned (* _ht_hash)(void*, void*); +typedef _common_compare _ht_compare; +typedef _common_operation _ht_operation; + +typedef struct _ht_node_t { + _ls_operation free_extra; + _ht_compare compare; + _ht_hash hash; + unsigned array_size; + unsigned count; + _ls_node_t** array; +} _ht_node_t; + +/** Normal enum/struct/union/const, etc. */ + +/* Error description text */ +static const char* _ERR_DESC[] = { + "No error", + /** Common */ + "Open MY-BASIC failed", + "A function with the same name already exists", + "A function with the name does not exists", + "Not supported", + /** Parsing */ + "Open file failed", + "Symbol too long", + "Invalid character", + /** Running */ + "Empty program", + "Program too long", + "Syntax error", + "Invalid data type", + "Type does not match", + "Number overflow", + "Invalid string", + "Index out of bound", + "Cannot find with given index", + "Illegal bound", + "Too many dimensions", + "Operation failed", + "Invalid operation usage", + "Dimension count out of bound", + "Rank out of bound", + "Complex array required", + "Label does not exist", + "No return point", + "Colon expected", + "Comma expected", + "Comma or semicolon expected", + "Array identifier expected", + "Open bracket expected", + "Close bracket expected", + "Array subscript expected", + "Nested too deep", + "Incomplete structure", + "Function expected", + "Variable or array identifier expected", + "Assign operator expected", + "String expected", + "Number expected", + "Integer expected", + "ELSE statement expected", + "TO statement expected", + "NEXT statement expected", + "UNTIL statement expected", + "Loop variable expected", + "Jump label expected", + "Variable expected", + "Invalid identifier usage", + "Duplicate identifier", + "Operator expected", + "Calculation error", + "Divide by zero", + "MOD by zero", + "Invalid expression", + "Out of memory", + "Module not match", + "Wrong function reached", + "Do not suspend in a routine", + "Do not mix instructional and structured sub routines", + "Invalid routine", + "Incomplete routine", + "Routine expected", + "Duplicate routine", + "To many routines", + "Invalid class", + "Incomplete class", + "Class expected", + "Duplicate class", + "Wrong meta class", + "HASH and COMPARE must come together", + "Cannot change ME", + "Invalid lambda", + "List expected", + "Collection expected", + "Iterator expected", + "Collection or iterator expected", + "Collection or iterator or class expected", + "Invalid iterator", + "Empty collection", + "Referenced type expected", + "Reference count overflow", + "Weak reference count overflow", + "Debug identifier not found", + "Stack trace disabled", + /** Extended abort */ + "Extended abort" +}; + +mb_static_assert(countof(_ERR_DESC) == SE_COUNT); + +/* Data type */ +typedef enum _data_e { + _DT_INVALID = -1, + _DT_NIL = 0, + _DT_UNKNOWN, + _DT_INT, + _DT_REAL, + _DT_STRING, + _DT_TYPE, + _DT_USERTYPE, +#ifdef MB_ENABLE_USERTYPE_REF + _DT_USERTYPE_REF, +#endif /* MB_ENABLE_USERTYPE_REF */ + _DT_FUNC, + _DT_VAR, + _DT_ARRAY, +#ifdef MB_ENABLE_COLLECTION_LIB + _DT_LIST, + _DT_LIST_IT, + _DT_DICT, + _DT_DICT_IT, +#endif /* MB_ENABLE_COLLECTION_LIB */ + _DT_LABEL, /* Label type, used for GOTO, GOSUB statement */ +#ifdef MB_ENABLE_CLASS + _DT_CLASS, /* Object instance */ +#endif /* MB_ENABLE_CLASS */ + _DT_ROUTINE, /* User defined sub routine in script */ +#ifdef MB_ENABLE_LAMBDA + _DT_OUTER_SCOPE, +#endif /* MB_ENABLE_LAMBDA */ + _DT_SEP, /* Separator */ +#ifdef MB_ENABLE_SOURCE_TRACE + _DT_PREV_IMPORT, + _DT_POST_IMPORT, +#endif /* MB_ENABLE_SOURCE_TRACE */ + _DT_EOS /* End of statement */ +} _data_e; + +#ifdef MB_ENABLE_COLLECTION_LIB +# define _HAS_REF_OBJ_LOCK +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifdef _HAS_REF_OBJ_LOCK +typedef short _lock_t; +#endif /* _HAS_REF_OBJ_LOCK */ + +struct _ref_t; + +typedef void (* _unref_func_t)(struct _ref_t*, void*); + +#define _NONE_REF 1 + +typedef unsigned _ref_count_t; + +/* The reference structure should be always at the head of an object */ +typedef struct _ref_t { + _ref_count_t* count; + _ref_count_t* weak_count; + _unref_func_t on_unref; + _data_e type; + struct mb_interpreter_t* s; +} _ref_t; + +typedef struct _gc_t { + _ht_node_t* table; + _ht_node_t* recursive_table; + _ht_node_t* collected_table; + _ht_node_t* valid_table; + int_t collecting; +} _gc_t; + +typedef struct _calculation_operator_info_t { + mb_meta_operator_t is; + mb_meta_operator_t add; + mb_meta_operator_t sub; + mb_meta_operator_t mul; + mb_meta_operator_t div; + mb_meta_operator_t neg; +} _calculation_operator_info_t; + +typedef struct _usertype_ref_t { + _ref_t ref; + void* usertype; + mb_dtor_func_t dtor; + mb_clone_func_t clone; + mb_hash_func_t hash; + mb_cmp_func_t cmp; + mb_fmt_func_t fmt; + _calculation_operator_info_t* calc_operators; + mb_meta_func_t coll_func; + mb_meta_func_t generic_func; +} _usertype_ref_t; + +typedef struct _func_t { + char* name; + mb_func_t pointer; +} _func_t; + +#define _PATHING_NONE 0 +#define _PATHING_NORMAL 1 +#define _PATHING_UNKNOWN_FOR_NOT_FOUND 2 + +#define _PN(__b) ((!!(__b)) ? (_PATHING_NORMAL) : (_PATHING_NONE)) +#define _PU(__b) ((!!(__b)) ? (_PATHING_UNKNOWN_FOR_NOT_FOUND) : (_PATHING_NONE)) + +typedef struct _var_t { + char* name; + struct _object_t* data; +#ifdef MB_ENABLE_CLASS + bool_t pathing; + bool_t isme; +#endif /* MB_ENABLE_CLASS */ +} _var_t; + +typedef struct _array_t { +#ifdef MB_ENABLE_ARRAY_REF + _ref_t ref; +#endif /* MB_ENABLE_ARRAY_REF */ + char* name; + _data_e type; +#ifndef MB_SIMPLE_ARRAY + _data_e* types; +#endif /* MB_SIMPLE_ARRAY */ + void* raw; + unsigned count; + unsigned char dimension_count; + unsigned dimensions[MB_MAX_DIMENSION_COUNT]; +} _array_t; + +#ifdef MB_ENABLE_COLLECTION_LIB +typedef struct _array_helper_t { + struct mb_interpreter_t* s; + _array_t* array; + int index; +} _array_helper_t; +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifdef MB_ENABLE_COLLECTION_LIB +typedef struct _list_t { + _ref_t ref; + _lock_t lock; + _ls_node_t* list; + _ls_node_t* cached_node; + int cached_index; + int_t count; + int_t* range_begin; +} _list_t; + +typedef struct _list_it_t { + _ref_t weak_ref; + _list_t* list; + bool_t locking; + union { + _ls_node_t* node; + int_t ranging; + } curr; +} _list_it_t; + +typedef struct _dict_t { + _ref_t ref; + _lock_t lock; + _ht_node_t* dict; +} _dict_t; + +#define _INVALID_DICT_IT ((_ls_node_t*)(intptr_t)~0) + +typedef struct _dict_it_t { + _ref_t weak_ref; + _dict_t* dict; + bool_t locking; + unsigned curr_bucket; + _ls_node_t* curr_node; +} _dict_it_t; +#endif /* MB_ENABLE_COLLECTION_LIB */ + +typedef struct _label_t { + char* name; + _ls_node_t* node; +} _label_t; + +#ifdef MB_ENABLE_CLASS +#define _META_LIST_MAX_DEPTH UINT_MAX + +#define _CLASS_ME "ME" + +#define _CLASS_HASH_FUNC "HASH" +#define _CLASS_COMPARE_FUNC "COMPARE" +#define _CLASS_TOSTRING_FUNC "TOSTRING" + +#define _CLASS_OVERRIDE_FMT "_%s" + +typedef struct _class_t { + _ref_t ref; + char* name; + struct _class_t* created_from; + _ls_node_t* meta_list; + struct _running_context_t* scope; + struct _routine_t* hash; + struct _routine_t* compare; + void* userdata; +} _class_t; +#endif /* MB_ENABLE_CLASS */ + +typedef enum _invokable_e { + _IT_SCRIPT = 1, +#ifdef MB_ENABLE_LAMBDA + _IT_LAMBDA, +#endif /* MB_ENABLE_LAMBDA */ + _IT_NATIVE +} _invokable_e; + +#ifdef MB_ENABLE_LAMBDA +typedef struct _running_context_ref_t { + _ref_t ref; + struct _running_context_ref_t* prev; + struct _running_context_t* scope; +} _running_context_ref_t; + +typedef struct _upvalue_scope_tuple_t { + struct mb_interpreter_t* s; +#ifdef MB_ENABLE_CLASS + _class_t* instance; +#endif /* MB_ENABLE_CLASS */ + struct _running_context_t* scope; + struct _running_context_ref_t* outer_scope; + struct _lambda_t* lambda; + _ht_node_t* filled; +} _upvalue_scope_tuple_t; + +typedef struct _lambda_t { + _ref_t ref; + struct _running_context_t* scope; + _ls_node_t* parameters; + struct _running_context_ref_t* outer_scope; + _ht_node_t* upvalues; + _ls_node_t* entry; + _ls_node_t* end; +} _lambda_t; +#endif /* MB_ENABLE_LAMBDA */ + +typedef struct _routine_t { + union { + struct { + struct _running_context_t* scope; + _ls_node_t* parameters; + _ls_node_t* entry; + } basic; +#ifdef MB_ENABLE_LAMBDA + _lambda_t lambda; +#endif /* MB_ENABLE_LAMBDA */ + struct { + mb_routine_func_t entry; + } native; + } func; + char* name; +#ifdef MB_ENABLE_SOURCE_TRACE + char* source_file; +#endif /* MB_ENABLE_SOURCE_TRACE */ +#ifdef MB_ENABLE_CLASS + _class_t* instance; +#endif /* MB_ENABLE_CLASS */ + bool_t is_cloned; + _invokable_e type; +} _routine_t; + +#ifdef MB_ENABLE_SOURCE_TRACE +typedef struct _import_info_t { + char* source_file; +} _import_info_t; +#endif /* MB_ENABLE_SOURCE_TRACE */ + +typedef union _raw_u { mb_data_e e; char c; int_t i; real_t r; void* p; mb_val_bytes_t b; } _raw_u; + +typedef unsigned char _raw_t[sizeof(_raw_u)]; + +typedef struct _object_t { + _data_e type; + union { + int_t integer; + real_t float_point; + char* string; + mb_data_e type; + void* usertype; + _usertype_ref_t* usertype_ref; + _func_t* func; + _var_t* variable; + _array_t* array; +#ifdef MB_ENABLE_COLLECTION_LIB + _list_t* list; + _list_it_t* list_it; + _dict_t* dict; + _dict_it_t* dict_it; +#endif /* MB_ENABLE_COLLECTION_LIB */ + _label_t* label; +#ifdef MB_ENABLE_CLASS + _class_t* instance; +#endif /* MB_ENABLE_CLASS */ + _routine_t* routine; + char separator; +#ifdef MB_ENABLE_SOURCE_TRACE + _import_info_t* import_info; +#endif /* MB_ENABLE_SOURCE_TRACE */ + mb_val_bytes_t bytes; + void* pointer; + _raw_t raw; + } data; + bool_t ref; +#ifdef MB_ENABLE_SOURCE_TRACE + int source_pos; + unsigned short source_row; + unsigned short source_col; +#else /* MB_ENABLE_SOURCE_TRACE */ + char source_pos; +#endif /* MB_ENABLE_SOURCE_TRACE */ +} _object_t; + +#ifdef MB_ENABLE_MODULE +typedef struct _module_func_t { + char* module; + mb_func_t func; +} _module_func_t; +#endif /* MB_ENABLE_MODULE */ + +typedef struct _dynamic_buffer_t { + char bytes[_TEMP_FORMAT_MAX_LENGTH]; + union { + char* charp; +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE + wchar_t* wcharp; +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ + } pointer; + size_t size; +} _dynamic_buffer_t; + +#define _MB_MEM_TAG_SIZE (sizeof(mb_mem_tag_t)) + +MBAPI size_t MB_SIZEOF_4BYTES = 4; +MBAPI size_t MB_SIZEOF_8BYTES = 8; +MBAPI size_t MB_SIZEOF_32BYTES = 32; +MBAPI size_t MB_SIZEOF_64BYTES = 64; +MBAPI size_t MB_SIZEOF_128BYTES = 128; +MBAPI size_t MB_SIZEOF_256BYTES = 256; +MBAPI size_t MB_SIZEOF_512BYTES = 512; +#ifdef MB_ENABLE_ALLOC_STAT +MBAPI size_t MB_SIZEOF_INT = _MB_MEM_TAG_SIZE + sizeof(int); +MBAPI size_t MB_SIZEOF_PTR = _MB_MEM_TAG_SIZE + sizeof(intptr_t); +MBAPI size_t MB_SIZEOF_LSN = _MB_MEM_TAG_SIZE + sizeof(_ls_node_t); +MBAPI size_t MB_SIZEOF_HTN = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t); +MBAPI size_t MB_SIZEOF_HTA = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT; +MBAPI size_t MB_SIZEOF_OBJ = _MB_MEM_TAG_SIZE + sizeof(_object_t); +#ifdef MB_ENABLE_USERTYPE_REF +MBAPI size_t MB_SIZEOF_UTR = _MB_MEM_TAG_SIZE + sizeof(_usertype_ref_t); +#endif /* MB_ENABLE_USERTYPE_REF */ +MBAPI size_t MB_SIZEOF_FUN = _MB_MEM_TAG_SIZE + sizeof(_func_t); +MBAPI size_t MB_SIZEOF_VAR = _MB_MEM_TAG_SIZE + sizeof(_var_t); +MBAPI size_t MB_SIZEOF_ARR = _MB_MEM_TAG_SIZE + sizeof(_array_t); +#ifdef MB_ENABLE_COLLECTION_LIB +MBAPI size_t MB_SIZEOF_LST = _MB_MEM_TAG_SIZE + sizeof(_list_t); +MBAPI size_t MB_SIZEOF_DCT = _MB_MEM_TAG_SIZE + sizeof(_dict_t); +#endif /* MB_ENABLE_COLLECTION_LIB */ +MBAPI size_t MB_SIZEOF_LBL = _MB_MEM_TAG_SIZE + sizeof(_label_t); +#ifdef MB_ENABLE_CLASS +MBAPI size_t MB_SIZEOF_CLS = _MB_MEM_TAG_SIZE + sizeof(_class_t); +#endif /* MB_ENABLE_CLASS */ +MBAPI size_t MB_SIZEOF_RTN = _MB_MEM_TAG_SIZE + sizeof(_routine_t); +#else /* MB_ENABLE_ALLOC_STAT */ +MBAPI size_t MB_SIZEOF_INT = sizeof(int); +MBAPI size_t MB_SIZEOF_PTR = sizeof(intptr_t); +MBAPI size_t MB_SIZEOF_LSN = sizeof(_ls_node_t); +MBAPI size_t MB_SIZEOF_HTN = sizeof(_ht_node_t); +MBAPI size_t MB_SIZEOF_HTA = sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT; +MBAPI size_t MB_SIZEOF_OBJ = sizeof(_object_t); +#ifdef MB_ENABLE_USERTYPE_REF +MBAPI size_t MB_SIZEOF_UTR = sizeof(_usertype_ref_t); +#endif /* MB_ENABLE_USERTYPE_REF */ +MBAPI size_t MB_SIZEOF_FUN = sizeof(_func_t); +MBAPI size_t MB_SIZEOF_VAR = sizeof(_var_t); +MBAPI size_t MB_SIZEOF_ARR = sizeof(_array_t); +#ifdef MB_ENABLE_COLLECTION_LIB +MBAPI size_t MB_SIZEOF_LST = sizeof(_list_t); +MBAPI size_t MB_SIZEOF_DCT = sizeof(_dict_t); +#endif /* MB_ENABLE_COLLECTION_LIB */ +MBAPI size_t MB_SIZEOF_LBL = sizeof(_label_t); +#ifdef MB_ENABLE_CLASS +MBAPI size_t MB_SIZEOF_CLS = sizeof(_class_t); +#endif /* MB_ENABLE_CLASS */ +MBAPI size_t MB_SIZEOF_RTN = sizeof(_routine_t); +#endif /* MB_ENABLE_ALLOC_STAT */ + +#ifdef MB_ENABLE_SOURCE_TRACE +static const _object_t _OBJ_INT_UNIT = { _DT_INT, (int_t)1, false, 0, 0, 0 }; +static const _object_t _OBJ_INT_ZERO = { _DT_INT, (int_t)0, false, 0, 0, 0 }; +#else /* MB_ENABLE_SOURCE_TRACE */ +static const _object_t _OBJ_INT_UNIT = { _DT_INT, (int_t)1, false, 0 }; +static const _object_t _OBJ_INT_ZERO = { _DT_INT, (int_t)0, false, 0 }; +#endif /* MB_ENABLE_SOURCE_TRACE */ +#define _MAKE_NIL(__o) do { memset((__o), 0, sizeof(_object_t)); (__o)->type = _DT_NIL; } while(0) + +static _object_t* _OBJ_BOOL_TRUE = 0; +static _object_t* _OBJ_BOOL_FALSE = 0; + +#ifdef MB_ENABLE_CLASS +#ifdef MB_ENABLE_SOURCE_TRACE +static const _object_t _OBJ_UNKNOWN = { _DT_UNKNOWN, (int_t)0, false, 0, 0, 0 }; +#else /* MB_ENABLE_SOURCE_TRACE */ +static const _object_t _OBJ_UNKNOWN = { _DT_UNKNOWN, (int_t)0, false, 0 }; +#endif /* MB_ENABLE_SOURCE_TRACE */ +static const _ls_node_t _LS_NODE_UNKNOWN = { (void*)&_OBJ_UNKNOWN, 0, 0, 0 }; +#endif /* MB_ENABLE_CLASS */ + +#define _VAR_ARGS_STR "..." + +#ifdef MB_ENABLE_CLASS +static const _var_t _VAR_ARGS = { _VAR_ARGS_STR, 0, 0, 0 }; +#else /* MB_ENABLE_CLASS */ +static const _var_t _VAR_ARGS = { _VAR_ARGS_STR, 0 }; +#endif /* MB_ENABLE_CLASS */ + +/* Parsing context */ +#define _CLASS_STATE_NONE 0 +#define _CLASS_STATE_PROC 1 + +static const char _MULTI_LINE_COMMENT_PREFIX[] = "'["; +static const char _MULTI_LINE_COMMENT_POSTFIX[] = "']"; + +typedef enum _parsing_state_e { + _PS_NORMAL, + _PS_STRING, + _PS_COMMENT, + _PS_MULTI_LINE_COMMENT +} _parsing_state_e; + +typedef enum _symbol_state_e { + _SS_IDENTIFIER, + _SS_OPERATOR +} _symbol_state_e; + +typedef struct _parsing_context_t { + _ls_node_t* imported; + char current_char; + char current_symbol[_SINGLE_SYMBOL_MAX_LENGTH + 1]; + int current_symbol_nonius; + int current_symbol_contains_accessor; + _object_t* last_symbol; + int multi_line_comment_count; + _parsing_state_e parsing_state; + _symbol_state_e symbol_state; +#ifdef MB_ENABLE_CLASS + unsigned short class_state; +#endif /* MB_ENABLE_CLASS */ + unsigned short routine_state; + unsigned short routine_params_state; + int parsing_pos; + unsigned short parsing_row; + unsigned short parsing_col; +} _parsing_context_t; + +/* Running context */ +#define _SCOPE_META_ROOT (1 << 0) +#define _SCOPE_META_REF (1 << 1) + +#define _INFINITY_CALC_DEPTH -1 + +typedef struct _running_context_t { + struct _running_context_t* prev; + unsigned meta; + _ht_node_t* var_dict; + struct _running_context_t* ref; + _var_t* next_loop_var; + mb_value_t intermediate_value; + int calc_depth; +#ifdef MB_ENABLE_LAMBDA + _ls_node_t* refered_lambdas; +#endif /* MB_ENABLE_LAMBDA */ +} _running_context_t; + +/* Expression processing utilities */ +typedef struct _tuple3_t { + void* e1; + void* e2; + void* e3; +} _tuple3_t; + +/* Interpreter tag */ +#define _JMP_NIL 0x00 +#define _JMP_INS 0x01 +#define _JMP_STR 0x02 + +typedef struct mb_interpreter_t { + /** Fundamental */ + bool_t valid; + void* userdata; + _ls_node_t* ast; + /** Memory management */ + _gc_t gc; + _ls_node_t* edge_destroy_objects; + _ls_node_t* lazy_destroy_objects; + /** Scripting interface and module */ + _ht_node_t* local_func_dict; + _ht_node_t* global_func_dict; +#ifdef MB_ENABLE_MODULE + _ht_node_t* module_func_dict; + char* with_module; + _ls_node_t* using_modules; +#endif /* MB_ENABLE_MODULE */ + /** Parsing and interpreting */ + char* source_file; + _parsing_context_t* parsing_context; + _running_context_t* running_context; + _ls_node_t* var_args; + unsigned char jump_set; +#ifdef MB_ENABLE_CLASS + _class_t* last_instance; + bool_t calling; +#endif /* MB_ENABLE_CLASS */ + _routine_t* last_routine; + _ls_node_t* sub_stack; + _ls_node_t* suspent_point; + int schedule_suspend_tag; + int_t no_eat_comma_mark; + _ls_node_t* skip_to_eoi; + _ls_node_t* in_neg_expr; +#ifdef MB_ENABLE_STACK_TRACE + _ls_node_t* stack_frames; +#endif /* MB_ENABLE_STACK_TRACE */ + /** Error handling */ + bool_t handled_error; + mb_error_e last_error; + char* last_error_file; + int last_error_pos; + unsigned short last_error_row; + unsigned short last_error_col; + /** Handlers */ + mb_debug_stepped_handler_t debug_stepped_handler; + mb_error_handler_t error_handler; + mb_print_func_t printer; + mb_input_func_t inputer; + mb_import_handler_t import_handler; +} mb_interpreter_t; + +/* Operations */ +static const char _PRECEDE_TABLE[20][20] = { /* Operator priority table */ + /* + - * / MOD ^ ( ) = > < >= <= == <> AND OR NOT NEG IS */ + { '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* + */ + { '>', '>', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* - */ + { '>', '>', '>', '>', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* * */ + { '>', '>', '>', '>', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* / */ + { '>', '>', '<', '<', '>', '<', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* MOD */ + { '>', '>', '>', '>', '>', '>', '<', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* ^ */ + { '<', '<', '<', '<', '<', '<', '<', '=', ' ', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<' }, /* ( */ + { '>', '>', '>', '>', '>', '>', ' ', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>', '>' }, /* ) */ + { '<', '<', '<', '<', '<', '<', '<', ' ', '=', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<', '<' }, /* = */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* > */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* < */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* >= */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* <= */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* == */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>', '>', '>' }, /* <> */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' }, /* AND */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' }, /* OR */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '>', '>', '>' }, /* NOT */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '<', '<', '<', '=', '<' }, /* NEG */ + { '<', '<', '<', '<', '<', '<', '<', '>', '>', '<', '<', '<', '<', '<', '<', '>', '>', '<', '>', '>' } /* IS */ +}; + +static _object_t* _exp_assign = 0; + +#define _set_real_with_hex(__r, __i) \ + do { \ + if(sizeof(__r) == sizeof(unsigned char)) { \ + unsigned char __b = __i; \ + memcpy(&(__r), &__b, sizeof(__r)); \ + } else if(sizeof(__r) == sizeof(unsigned short)) { \ + unsigned short __b = __i; \ + memcpy(&(__r), &__b, sizeof(__r)); \ + } else if(sizeof(__r) == sizeof(unsigned)) { \ + unsigned __b = __i; \ + memcpy(&(__r), &__b, sizeof(__r)); \ + } else if(sizeof(__r) == sizeof(unsigned long)) { \ + unsigned long __b = __i; \ + memcpy(&(__r), &__b, sizeof(__r)); \ + } else if(sizeof(__r) == sizeof(unsigned long long)) { \ + unsigned long long __b = __i; \ + memcpy(&(__r), &__b, sizeof(__r)); \ + } else { \ + mb_assert(0 && "Invalid real number precision."); \ + } \ + } while(0) + +#if MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE +# define _convert_to_int_if_posible(__o) do { ((void)(__o)); } while(0) +#else /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ +# define _convert_to_int_if_posible(__o) \ + do { \ + if((__o)->type == _DT_REAL && (real_t)(int_t)(__o)->data.float_point == (__o)->data.float_point) { \ + (__o)->type = _DT_INT; \ + (__o)->data.integer = (int_t)(__o)->data.float_point; \ + } \ + } while(0) +#endif /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ + +#define _instruct_head(__tuple) \ + _object_t opndv1; \ + _object_t opndv2; \ + _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ + _object_t* opnd1 = (_object_t*)(tpptr->e1); \ + _object_t* opnd2 = (_object_t*)(tpptr->e2); \ + _object_t* val = (_object_t*)(tpptr->e3); +#define _instruct_common(__tuple) \ + _instruct_head(__tuple) \ + opndv1.type = (opnd1->type == _DT_INT || (opnd1->type == _DT_VAR && opnd1->data.variable->data->type == _DT_INT)) ? \ + _DT_INT : _DT_REAL; \ + opndv1.data = opnd1->type == _DT_VAR ? opnd1->data.variable->data->data : opnd1->data; \ + opndv2.type = (opnd2->type == _DT_INT || (opnd2->type == _DT_VAR && opnd2->data.variable->data->type == _DT_INT)) ? \ + _DT_INT : _DT_REAL; \ + opndv2.data = opnd2->type == _DT_VAR ? opnd2->data.variable->data->data : opnd2->data; +#define _instruct_fun_num_num(__optr, __tuple) \ + do { \ + _instruct_common(__tuple) \ + if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t)__optr((real_t)opndv1.data.integer, (real_t)opndv2.data.integer); \ + } else { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t)__optr( \ + opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point, \ + opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point); \ + } \ + _convert_to_int_if_posible(val); \ + } while(0) +#define _instruct_bool_op_bool(__optr, __tuple) \ + do { \ + _instruct_common(__tuple) \ + if(opndv1.type == _DT_NIL) { opndv1.type = _DT_INT; opndv1.data.integer = 0; } \ + else if(opndv1.type != _DT_INT && opndv1.type != _DT_REAL) { opndv1.type = _DT_INT; opndv1.data.integer = 1; } \ + if(opndv2.type == _DT_NIL) { opndv2.type = _DT_INT; opndv2.data.integer = 0; } \ + else if(opndv2.type != _DT_INT && opndv2.type != _DT_REAL) { opndv2.type = _DT_INT; opndv2.data.integer = 1; } \ + if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ + if((real_t)(opndv1.data.integer __optr opndv2.data.integer) == (real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer) { \ + val->type = _DT_INT; \ + val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ + } else { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t)((real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer); \ + } \ + } else { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t) \ + ((opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point) __optr \ + (opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point)); \ + } \ + _convert_to_int_if_posible(val); \ + } while(0) +#define _instruct_int_op_int(__optr, __tuple) \ + do { \ + _instruct_common(__tuple) \ + if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ + val->type = _DT_INT; \ + val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ + } else { \ + val->type = _DT_INT; \ + val->data.integer = \ + ((opndv1.type == _DT_INT ? opndv1.data.integer : (int_t)(opndv1.data.float_point)) __optr \ + (opndv2.type == _DT_INT ? opndv2.data.integer : (int_t)(opndv2.data.float_point))); \ + } \ + } while(0) +#define _instruct_num_op_num(__optr, __tuple) \ + do { \ + _instruct_common(__tuple) \ + if(opndv1.type == _DT_INT && opndv2.type == _DT_INT) { \ + if((real_t)(opndv1.data.integer __optr opndv2.data.integer) == (real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer) { \ + val->type = _DT_INT; \ + val->data.integer = opndv1.data.integer __optr opndv2.data.integer; \ + } else { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t)((real_t)opndv1.data.integer __optr (real_t)opndv2.data.integer); \ + } \ + } else { \ + val->type = _DT_REAL; \ + val->data.float_point = (real_t) \ + ((opndv1.type == _DT_INT ? opndv1.data.integer : opndv1.data.float_point) __optr \ + (opndv2.type == _DT_INT ? opndv2.data.integer : opndv2.data.float_point)); \ + } \ + _convert_to_int_if_posible(val); \ + } while(0) +#define _instruct_obj_op_obj(__optr, __tuple) \ + do { \ + _instruct_head(__tuple); \ + opndv1.type = opnd1->type == _DT_VAR ? opnd1->data.variable->data->type : opnd1->type; \ + opndv1.data = opnd1->type == _DT_VAR ? opnd1->data.variable->data->data : opnd1->data; \ + opndv2.type = opnd2->type == _DT_VAR ? opnd2->data.variable->data->type : opnd2->type; \ + opndv2.data = opnd2->type == _DT_VAR ? opnd2->data.variable->data->data : opnd2->data; \ + val->type = _DT_INT; \ + if(opndv1.type == opndv2.type) { \ + val->data.integer = (int_t)(mb_memcmp(&opndv1.data, &opndv2.data, sizeof(_raw_t)) __optr 0); \ + } else { \ + val->data.integer = (int_t)(opndv1.type __optr opndv2.type); \ + } \ + } while(0) +#define _instruct_connect_strings(__tuple) \ + do { \ + char* _str1 = 0; \ + char* _str2 = 0; \ + _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ + _object_t* opnd1 = (_object_t*)(tpptr->e1); \ + _object_t* opnd2 = (_object_t*)(tpptr->e2); \ + _object_t* val = (_object_t*)(tpptr->e3); \ + val->type = _DT_STRING; \ + if(val->data.string) { \ + safe_free(val->data.string); \ + } \ + _str1 = _extract_string(opnd1); \ + _str2 = _extract_string(opnd2); \ + val->data.string = (char*)mb_malloc(strlen(_str1) + strlen(_str2) + 1); \ + memset(val->data.string, 0, strlen(_str1) + strlen(_str2) + 1); \ + strcat(val->data.string, _str1); \ + strcat(val->data.string, _str2); \ + } while(0) +#define _instruct_compare_strings(__optr, __tuple) \ + do { \ + char* _str1 = 0; \ + char* _str2 = 0; \ + _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ + _object_t* opnd1 = (_object_t*)(tpptr->e1); \ + _object_t* opnd2 = (_object_t*)(tpptr->e2); \ + _object_t* val = (_object_t*)(tpptr->e3); \ + val->type = _DT_INT; \ + _str1 = _extract_string(opnd1); \ + _str2 = _extract_string(opnd2); \ + val->data.integer = strcmp(_str1, _str2) __optr 0; \ + } while(0) +#ifdef MB_ENABLE_USERTYPE_REF +# define _instruct_obj_meta_obj(__optr, __tuple, __result, __exit) \ + do { \ + _tuple3_t* tpptr = (_tuple3_t*)(*__tuple); \ + _object_t* opnd1 = (_object_t*)(tpptr->e1); \ + _object_t* opnd2 = (_object_t*)(tpptr->e2); \ + _object_t* retval = (_object_t*)(tpptr->e3); \ + if(opnd1->type == _DT_VAR) opnd1 = opnd1->data.variable->data; \ + if(opnd2->type == _DT_VAR) opnd2 = opnd2->data.variable->data; \ + { \ + mb_value_t vfst, vscd; \ + mb_value_t ret; \ + mb_make_nil(vfst); \ + mb_make_nil(vscd); \ + mb_make_nil(ret); \ + if(opnd1->type == _DT_USERTYPE_REF && opnd1->data.usertype_ref->calc_operators && opnd1->data.usertype_ref->calc_operators->__optr) { \ + _internal_object_to_public_value(opnd1, &vfst); \ + _internal_object_to_public_value(opnd2, &vscd); \ + __result = opnd1->data.usertype_ref->calc_operators->__optr(s, (__tuple), &vfst, &vscd, &ret); \ + _public_value_to_internal_object(&ret, retval); \ + goto __exit; \ + } else if(opnd2->type == _DT_USERTYPE_REF && opnd2->data.usertype_ref->calc_operators && opnd2->data.usertype_ref->calc_operators->__optr) { \ + _internal_object_to_public_value(opnd1, &vfst); \ + _internal_object_to_public_value(opnd2, &vscd); \ + __result = opnd2->data.usertype_ref->calc_operators->__optr(s, (__tuple), &vfst, &vscd, &ret); \ + _public_value_to_internal_object(&ret, retval); \ + goto __exit; \ + } \ + } \ + } while(0) +#else /* MB_ENABLE_USERTYPE_REF */ +# define _instruct_obj_meta_obj(__optr, __tuple, __result, __exit) do { ((void)(__tuple)); ((void)(__result)); } while(0) +#endif /* MB_ENABLE_USERTYPE_REF */ +#define _proc_div_by_zero(__s, __tuple, __exit, __result, __kind) \ + do { \ + _instruct_common(__tuple) \ + if((opndv2.type == _DT_INT && opndv2.data.integer == 0) || (opndv2.type == _DT_REAL && opndv2.data.float_point == 0.0f)) { \ + if((opndv1.type == _DT_INT && opndv1.data.integer == 0) || (opndv1.type == _DT_REAL && opndv1.data.float_point == 0.0f)) { \ + val->type = _DT_REAL; \ + _set_real_with_hex(val->data.float_point, MB_FNAN); \ + } else { \ + val->type = _DT_REAL; \ + _set_real_with_hex(val->data.float_point, MB_FINF); \ + } \ + _handle_error_on_obj((__s), __kind, (__s)->source_file, ((__tuple) && *(__tuple)) ? ((_object_t*)(((_tuple3_t*)(*(__tuple)))->e1)) : 0, MB_FUNC_WARNING, __exit, __result); \ + } \ + } while(0) + +#define _set_tuple3_result(__l, __r) \ + do { \ + _object_t* val = (_object_t*)(((_tuple3_t*)(*(__l)))->e3); \ + val->type = _DT_INT; \ + val->data.integer = __r; \ + } while(0) + +#define _math_calculate_fun_real(__s, __l, __a, __f, __exit, __result) \ + do { \ + switch((__a).type) { \ + case MB_DT_INT: \ + (__a).value.float_point = (real_t)__f((real_t)(__a).value.integer); \ + (__a).type = MB_DT_REAL; \ + break; \ + case MB_DT_REAL: \ + (__a).value.float_point = (real_t)__f((__a).value.float_point); \ + break; \ + default: \ + _handle_error_on_obj(__s, SE_RN_NUMBER_EXPECTED, (__s)->source_file, ((__l) && *(__l)) ? ((_object_t*)(((_tuple3_t*)(*(__l)))->e1)) : 0, MB_FUNC_WARNING, __exit, __result); \ + break; \ + } \ + mb_convert_to_int_if_posible(__a); \ + } while(0) + +#define _using_jump_set_of_instructional(__s, __obj, __exit, __result) \ + do { \ + if((__s)->jump_set & (~_JMP_INS)) { \ + _handle_error_on_obj(__s, SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, (__s)->source_file, DON(__obj), MB_FUNC_ERR, __exit, __result); \ + } else { \ + (__s)->jump_set |= _JMP_INS; \ + } \ + } while(0) +#define _using_jump_set_of_structured(__s, __obj, __exit, __result) \ + do { \ + if((__s)->jump_set & (~_JMP_STR)) { \ + _handle_error_on_obj(__s, SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, (__s)->source_file, DON(__obj), MB_FUNC_ERR, __exit, __result); \ + } else { \ + (__s)->jump_set |= _JMP_STR; \ + } \ + } while(0) + +/* ========================================================} */ + +/* +** {======================================================== +** Private function declarations +*/ + +/** List operations */ + +static int _ls_cmp_data(void* node, void* info); +static int _ls_cmp_extra(void* node, void* info); +static int _ls_cmp_extra_object(void* node, void* info); +static int _ls_cmp_extra_string(void* node, void* info); +#ifdef MB_ENABLE_MODULE +static int _ls_cmp_module_func(void* node, void* info); +#endif /* MB_ENABLE_MODULE */ + +static _ls_node_t* _ls_create_node(void* data); +static _ls_node_t* _ls_create(void); +static _ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp, int* idx); +static _ls_node_t* _ls_back(_ls_node_t* node); +static _ls_node_t* _ls_pushback(_ls_node_t* list, void* data); +static void* _ls_popback(_ls_node_t* list); +static _ls_node_t* _ls_front(_ls_node_t* node); +static void* _ls_popfront(_ls_node_t* list); +static _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data); +static unsigned _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op); +static unsigned _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op); +static unsigned _ls_foreach(_ls_node_t* list, _ls_operation op); +static _ls_node_t* _ls_sort(_ls_node_t* _mb_unaligned * list, _ls_compare cmp); +static unsigned _ls_count(_ls_node_t* list); +static bool_t _ls_empty(_ls_node_t* list); +static void _ls_clear(_ls_node_t* list); +static void _ls_destroy(_ls_node_t* list); +static int _ls_free_extra(void* data, void* extra); +#define _LS_FOREACH(L, O, P, E) \ + do { \ + _ls_node_t* __lst = L; \ + int __opresult = _OP_RESULT_NORMAL; \ + _ls_node_t* __tmp = 0; \ + mb_assert(L); \ + __lst = __lst->next; \ + while(__lst) { \ + if(P != 0) { \ + P(__lst->data, __lst->extra, E); \ + } \ + if(O != 0) { \ + __opresult = O(__lst->data, __lst->extra); \ + } \ + __tmp = __lst; \ + __lst = __lst->next; \ + if(_OP_RESULT_DEL_NODE == __opresult) { \ + __tmp->prev->next = __lst; \ + if(__lst) { \ + __lst->prev = __tmp->prev; \ + } \ + safe_free(__tmp); \ + (L)->data = (char*)(L)->data - 1; \ + } \ + } \ + } while(0) + +/** Dictionary operations */ + +static unsigned _ht_hash_object(void* ht, void* d); +static unsigned _ht_hash_string(void* ht, void* d); +static unsigned _ht_hash_intptr(void* ht, void* d); +static unsigned _ht_hash_ref(void* ht, void* d); + +static int _ht_cmp_object(void* d1, void* d2); +static int _ht_cmp_string(void* d1, void* d2); +static int _ht_cmp_intptr(void* d1, void* d2); +static int _ht_cmp_ref(void* d1, void* d2); + +static _ht_node_t* _ht_create(unsigned size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra); +static _ls_node_t* _ht_find(_ht_node_t* ht, void* key); +static unsigned _ht_set_or_insert(_ht_node_t* ht, void* key, void* value); +static unsigned _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp); +static unsigned _ht_foreach(_ht_node_t* ht, _ht_operation op); +static unsigned _ht_count(_ht_node_t* ht); +static void _ht_clear(_ht_node_t* ht); +static void _ht_destroy(_ht_node_t* ht); +static int _ht_remove_exist(void* data, void* extra, _ht_node_t* ht); +#define _HT_FOREACH(H, O, P, E) \ + do { \ + _ls_node_t* __bucket = 0; \ + unsigned __ul = 0; \ + if((H)->array) { \ + for(__ul = 0; __ul < (H)->array_size; ++__ul) { \ + __bucket = (H)->array[__ul]; \ + if(__bucket) { \ + _LS_FOREACH(__bucket, O, P, E); \ + } \ + } \ + } \ + } while(0) + +/** Memory manipulations */ + +static void _init_dynamic_buffer(_dynamic_buffer_t* buf); +static void _dispose_dynamic_buffer(_dynamic_buffer_t* buf); +static size_t _countof_dynamic_buffer(_dynamic_buffer_t* buf, size_t es); +static void _resize_dynamic_buffer(_dynamic_buffer_t* buf, size_t es, size_t c); + +#define _INIT_BUF(b) do { _init_dynamic_buffer(&(b)); } while(0) +#define _DISPOSE_BUF(b) do { _dispose_dynamic_buffer(&(b)); } while(0) +#define _CHARS_OF_BUF(b) (_countof_dynamic_buffer((&b), sizeof(char))) +#define _RESIZE_CHAR_BUF(b, c) do { _resize_dynamic_buffer(&(b), sizeof(char), (c)); } while(0) +#define _HEAP_CHAR_BUF(b) (((b).pointer.charp != (b).bytes) ? ((b).pointer.charp) : (mb_memdup((b).pointer.charp, (unsigned)(b).size))) +#define _CHAR_BUF_PTR(b) ((b).pointer.charp) +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE +#define _WCHARS_OF_BUF(b) (_countof_dynamic_buffer((&b), sizeof(wchar_t))) +#define _RESIZE_WCHAR_BUF(b, c) do { _resize_dynamic_buffer(&(b), sizeof(wchar_t), (c)); } while(0) +#define _WCHAR_BUF_PTR(b) ((b).pointer.wcharp) +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ + +#define _MB_CHECK_MEM_TAG_SIZE(y, s) ((mb_mem_tag_t)(s) == (s)) +#define _MB_WRITE_MEM_TAG_SIZE(t, s) (*((mb_mem_tag_t*)((char*)(t) - _MB_MEM_TAG_SIZE)) = (mb_mem_tag_t)(s)) +#define _MB_READ_MEM_TAG_SIZE(t) (*((mb_mem_tag_t*)((char*)(t) - _MB_MEM_TAG_SIZE))) + +#ifdef MB_ENABLE_ALLOC_STAT +static volatile size_t _mb_allocated = 0; +#else /* MB_ENABLE_ALLOC_STAT */ +static const size_t _mb_allocated = (size_t)(~0); +#endif /* MB_ENABLE_ALLOC_STAT */ + +static mb_memory_allocate_func_t _mb_allocate_func = 0; +static mb_memory_free_func_t _mb_free_func = 0; + +static void* mb_malloc(size_t s); +static void mb_free(void* p); + +static int mb_memcmp(void* l, void* r, size_t s); +static size_t mb_memtest(void* p, size_t s); + +static char* mb_strdup(const char* 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) + +static bool_t mb_is_little_endian(void); + +/** Unicode handling */ + +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE +static int mb_bytes_to_wchar(const char* sz, wchar_t** out, size_t size); +static int mb_bytes_to_wchar_ansi(const char* sz, wchar_t** out, size_t size); +static int mb_wchar_to_bytes(const wchar_t* sz, char** out, size_t size); +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ + +static int mb_uu_getbom(const char** ch); +#ifdef MB_ENABLE_UNICODE +static int mb_uu_ischar(const char* ch); +static int mb_uu_strlen(const char* ch); +static int mb_uu_substr(const char* ch, int begin, int count, char** o); +#endif /* MB_ENABLE_UNICODE */ + +/** Expression processing */ + +static bool_t _is_operator(mb_func_t op); +static bool_t _is_flow(mb_func_t op); +static bool_t _is_unary(mb_func_t op); +static bool_t _is_binary(mb_func_t op); +static char _get_priority(mb_func_t op1, mb_func_t op2); +static int _get_priority_index(mb_func_t op); +static _object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status); +static bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj); +static bool_t _is_unexpected_calc_type(mb_interpreter_t* s, _object_t* obj); +static bool_t _is_referenced_calc_type(mb_interpreter_t* s, _object_t* obj); +static int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val); +static _ls_node_t* _push_var_args(mb_interpreter_t* s); +static void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args); +static int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg); +static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args); +static int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); +static int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); +#ifdef MB_ENABLE_LAMBDA +static int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); +#endif /* MB_ENABLE_LAMBDA */ +static int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); +static int _has_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r); +static int _pop_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val); +static int _has_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r); +static int _pop_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val); +static bool_t _is_print_terminal(mb_interpreter_t* s, _object_t* obj); +static mb_meta_status_u _try_overridden(mb_interpreter_t* s, void** l, mb_value_t* d, const char* f, mb_meta_func_u t); + +/** Handlers */ + +#define _handle_error_now(__s, __err, __f, __result) \ + do { \ + _set_current_error((__s), (__err), (__f)); \ + if((__s)->error_handler) { \ + if((__s)->handled_error) \ + break; \ + (__s)->handled_error = true; \ + ((__s)->error_handler)((__s), (__s)->last_error, (char*)mb_get_error_desc((__s)->last_error), \ + (__s)->last_error_file, \ + (__s)->parsing_context ? (__s)->parsing_context->parsing_pos : (__s)->last_error_pos, \ + (__s)->parsing_context ? (__s)->parsing_context->parsing_row : (__s)->last_error_row, \ + (__s)->parsing_context ? (__s)->parsing_context->parsing_col : (__s)->last_error_col, \ + (__result)); \ + } \ + } while(0) +#if _WARNING_AS_ERROR +# define _handle_error_at_pos(__s, __err, __f, __pos, __row, __col, __ret, __exit, __result) \ + do { \ + _set_current_error((__s), (__err), (__f)); \ + _set_error_pos((__s), (__pos), (__row), (__col)); \ + __result = (__ret); \ + goto __exit; \ + } while(0) +#else /* _WARNING_AS_ERROR */ +# define _handle_error_at_pos(__s, __err, __f, __pos, __row, __col, __ret, __exit, __result) \ + do { \ + _set_current_error((__s), (__err), (__f)); \ + _set_error_pos((__s), (__pos), (__row), (__col)); \ + if((__ret) != MB_FUNC_WARNING) { \ + __result = (__ret); \ + } \ + goto __exit; \ + } while(0) +#endif /* _WARNING_AS_ERROR */ +#ifdef MB_ENABLE_SOURCE_TRACE +# define _handle_error_on_obj(__s, __err, __f, __obj, __ret, __exit, __result) \ + do { \ + if(__obj) { \ + _handle_error_at_pos((__s), (__err), (__f), (__obj)->source_pos, (__obj)->source_row, (__obj)->source_col, (__ret), __exit, (__result)); \ + } else { \ + _handle_error_at_pos((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result)); \ + } \ + } while(0) +#else /* MB_ENABLE_SOURCE_TRACE */ +# define _handle_error_on_obj(__s, __err, __f, __obj, __ret, __exit, __result) \ + do { ((void)(__obj)); _handle_error_at_pos((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result)); } while(0) +#endif /* MB_ENABLE_SOURCE_TRACE */ + +#define _OUTTER_SCOPE(__s) ((__s)->prev ? (__s)->prev : (__s)) + +static void _set_current_error(mb_interpreter_t* s, mb_error_e err, char* f); + +static mb_print_func_t _get_printer(mb_interpreter_t* s); +static mb_input_func_t _get_inputer(mb_interpreter_t* s); + +static void _print_string(mb_interpreter_t* s, _object_t* obj); + +/** Parsing helpers */ + +static char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix); +static void _end_of_file(_parsing_context_t* context); + +#define _ZERO_CHAR '\0' +#define _NEWLINE_CHAR '\n' +#define _RETURN_CHAR '\r' +#define _STRING_POSTFIX_CHAR '$' +#define _DUMMY_ASSIGN_CHAR "#" + +#define _REMARK_STR "REM" + +static bool_t _is_blank_char(char c); +static bool_t _is_eof_char(char c); +static bool_t _is_newline_char(char c); +static bool_t _is_separator_char(char c); +static bool_t _is_bracket_char(char c); +static bool_t _is_quotation_char(char c); +static bool_t _is_comment_char(char c); +static bool_t _is_accessor_char(char c); +static bool_t _is_numeric_char(char c); +static bool_t _is_identifier_char(char c); +static bool_t _is_operator_char(char c); +static bool_t _is_exponential_char(char c); +static bool_t _is_using_char(char c); +static bool_t _is_exponent_prefix(char* s, int begin, int end); + +static int _append_char_to_symbol(mb_interpreter_t* s, char c); +#ifdef MB_ENABLE_UNICODE_ID +static int _append_uu_char_to_symbol(mb_interpreter_t* s, const char* str, int n); +#endif /* MB_ENABLE_UNICODE_ID */ +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, _raw_t* value); +static int _parse_char(mb_interpreter_t* s, const char* str, int n, 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); +static char* _prev_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col); +static char* _post_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col); + +/** Object processors */ + +static int_t _get_size_of(_data_e type); +static bool_t _try_get_value(_object_t* obj, mb_value_u* val, _data_e expected); + +static bool_t _is_number(void* obj); +static bool_t _is_string(void* obj); +static char* _extract_string(_object_t* obj); + +#ifdef MB_ENABLE_COLLECTION_LIB +# define _IS_LIST(__o) ((__o) && ((_object_t*)(__o))->type == _DT_LIST) +# define _IS_DICT(__o) ((__o) && ((_object_t*)(__o))->type == _DT_DICT) +# define _IS_COLL(__o) (_IS_LIST(__o) || _IS_DICT(__o)) +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS +# define _IS_CLASS(__o) ((__o) && ((_object_t*)(__o))->type == _DT_CLASS) +# define _GET_CLASS(__o) ((!__o) ? 0 : (_IS_CLASS(__o) ? (__o) : (_IS_VAR(__o) && _IS_CLASS((__o)->data.variable->data) ? (__o)->data.variable->data : 0))) +# define _IS_ME(__v) (!!(__v)->isme) +#else /* MB_ENABLE_CLASS */ +# define _IS_ME(__v) false +#endif /* MB_ENABLE_CLASS */ +#define _IS_ROUTINE(__o) ((__o) && ((_object_t*)(__o))->type == _DT_ROUTINE) +#define _GET_ROUTINE(__o) ((!__o) ? 0 : (_IS_ROUTINE(__o) ? (__o) : (_IS_VAR(__o) && _IS_ROUTINE((__o)->data.variable->data) ? (__o)->data.variable->data : 0))) + +#ifdef MB_ENABLE_USERTYPE_REF +# define _REF_USERTYPE_REF(__o) \ + case _DT_USERTYPE_REF: \ + _ref(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref); \ + break; +# define _UNREF_USERTYPE_REF(__o) \ + case _DT_USERTYPE_REF: \ + _unref(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref); \ + break; +# define _ADDGC_USERTYPE_REF(__o, __g) \ + case _DT_USERTYPE_REF: \ + _gc_add(&(__o)->data.usertype_ref->ref, (__o)->data.usertype_ref, (__g)); \ + break; +#else /* MB_ENABLE_USERTYPE_REF */ +# define _REF_USERTYPE_REF(__o) { (void)(__o); } +# define _UNREF_USERTYPE_REF(__o) { (void)(__o); } +# define _ADDGC_USERTYPE_REF(__o, __g) { (void)(__o); (void)(__g); } +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_ARRAY_REF +# define _REF_ARRAY(__o) \ + case _DT_ARRAY: \ + if(!(__o)->ref) \ + _ref(&(__o)->data.array->ref, (__o)->data.array); \ + break; +# define _UNREF_ARRAY(__o) \ + case _DT_ARRAY: \ + if(!(__o)->ref) \ + _unref(&(__o)->data.array->ref, (__o)->data.array); \ + break; +# define _ADDGC_ARRAY(__o, __g) \ + case _DT_ARRAY: \ + if(!(__o)->ref) \ + _gc_add(&(__o)->data.array->ref, (__o)->data.array, (__g)); \ + break; +#else /* MB_ENABLE_ARRAY_REF */ +# define _REF_ARRAY(__o) { (void)(__o); } +# define _UNREF_ARRAY(__o) { (void)(__o); } +# define _ADDGC_ARRAY(__o, __g) { (void)(__o); (void)(__g); } +#endif /* MB_ENABLE_ARRAY_REF */ +#ifdef MB_ENABLE_COLLECTION_LIB +# define _REF_COLL(__o) \ + case _DT_LIST: \ + _ref(&(__o)->data.list->ref, (__o)->data.list); \ + break; \ + case _DT_DICT: \ + _ref(&(__o)->data.dict->ref, (__o)->data.dict); \ + break; +# define _UNREF_COLL(__o) \ + case _DT_LIST: \ + _unref(&(__o)->data.list->ref, (__o)->data.list); \ + break; \ + case _DT_DICT: \ + _unref(&(__o)->data.dict->ref, (__o)->data.dict); \ + break; +# define _ADDGC_COLL(__o, __g) \ + case _DT_LIST: \ + _gc_add(&(__o)->data.list->ref, (__o)->data.list, (__g)); \ + break; \ + case _DT_DICT: \ + _gc_add(&(__o)->data.dict->ref, (__o)->data.dict, (__g)); \ + break; +# define _UNREF_COLL_IT(__o) \ + case _DT_LIST_IT: \ + _destroy_list_it(obj->data.list_it); \ + break; \ + case _DT_DICT_IT: \ + _destroy_dict_it(obj->data.dict_it); \ + break; +# define _ADDGC_COLL_IT(__o, __g) \ + case _DT_LIST_IT: \ + _destroy_list_it(obj->data.list_it); \ + break; \ + case _DT_DICT_IT: \ + _destroy_dict_it(obj->data.dict_it); \ + break; +#else /* MB_ENABLE_COLLECTION_LIB */ +# define _REF_COLL(__o) { (void)(__o); } +# define _UNREF_COLL(__o) { (void)(__o); } +# define _ADDGC_COLL(__o, __g) { (void)(__o); (void)(__g); } +# define _UNREF_COLL_IT(__o) { (void)(__o); } +# define _ADDGC_COLL_IT(__o, __g) { (void)(__o); (void)(__g); } +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS +# define _REF_CLASS(__o) \ + case _DT_CLASS: \ + _ref(&(__o)->data.instance->ref, (__o)->data.instance); \ + break; +# define _UNREF_CLASS(__o) \ + case _DT_CLASS: \ + if(!(__o)->ref) \ + _unref(&(__o)->data.instance->ref, (__o)->data.instance); \ + break; +# define _ADDGC_CLASS(__o, __g) \ + case _DT_CLASS: \ + if(!(__o)->ref) \ + _gc_add(&(__o)->data.instance->ref, (__o)->data.instance, (__g)); \ + break; +#else /* MB_ENABLE_CLASS */ +# define _REF_CLASS(__o) { (void)(__o); } +# define _UNREF_CLASS(__o) { (void)(__o); } +# define _ADDGC_CLASS(__o, __g) { (void)(__o); (void)(__g); } +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_LAMBDA +# define _REF_ROUTINE(__o) \ + case _DT_ROUTINE: \ + _ref(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine); \ + break; +# define _UNREF_ROUTINE(__o) \ + case _DT_ROUTINE: \ + if(!(__o)->ref && (__o)->data.routine->type == _IT_LAMBDA) \ + _unref(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine); \ + else if(!(__o)->ref && (__o)->data.routine->type != _IT_LAMBDA) \ + _destroy_routine(0, (__o)->data.routine); \ + break; +# define _ADDGC_ROUTINE(__o, __g) \ + case _DT_ROUTINE: \ + if(!(__o)->ref && (__o)->data.routine->type == _IT_LAMBDA) \ + _gc_add(&(__o)->data.routine->func.lambda.ref, (__o)->data.routine, (__g)); \ + else if(!(__o)->ref && (__o)->data.routine->type != _IT_LAMBDA) \ + _dispose_object(__o); \ + break; +#else /* MB_ENABLE_LAMBDA */ +# define _REF_ROUTINE(__o) { (void)(__o); } +# define _UNREF_ROUTINE(__o) { (void)(__o); } +# define _ADDGC_ROUTINE(__o, __g) \ + case _DT_ROUTINE: \ + ((void)(__g)); \ + _dispose_object(__o); \ + break; +#endif /* MB_ENABLE_LAMBDA */ +#define _ADDGC_STRING(__o) \ + case _DT_STRING: \ + _dispose_object(__o); \ + break; +#define _REF(__o) \ + switch((__o)->type) { \ + _REF_USERTYPE_REF(__o) \ + _REF_ARRAY(__o) \ + _REF_COLL(__o) \ + _REF_CLASS(__o) \ + _REF_ROUTINE(__o) \ + default: break; \ + } +#define _UNREF(__o) \ + switch((__o)->type) { \ + _UNREF_USERTYPE_REF(__o) \ + _UNREF_ARRAY(__o) \ + _UNREF_COLL(__o) \ + _UNREF_CLASS(__o) \ + _UNREF_ROUTINE(__o) \ + default: break; \ + } +#define _ADDGC(__o, __g) \ + if(!(__o)->data.pointer || !_ht_find((__g)->collected_table, (__o)->data.pointer)) { \ + switch((__o)->type) { \ + _ADDGC_USERTYPE_REF(__o, __g) \ + _ADDGC_ARRAY(__o, __g) \ + _ADDGC_COLL(__o, __g) \ + _ADDGC_COLL_IT(__o, __g) \ + _ADDGC_CLASS(__o, __g) \ + _ADDGC_ROUTINE(__o, __g) \ + _ADDGC_STRING(__o) \ + default: break; \ + } \ + } + +#ifdef _HAS_REF_OBJ_LOCK +static bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); +static bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj); +static bool_t _write_on_ref_object(_lock_t* lk, _ref_t* ref, void* obj); +#endif /* _HAS_REF_OBJ_LOCK */ + +static _ref_count_t _ref(_ref_t* ref, void* data); +static bool_t _unref(_ref_t* ref, void* data); +static _ref_count_t _weak_ref(_ref_t* ref, void* data, _ref_t* weak); +static bool_t _weak_unref(_ref_t* weak); +static void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpreter_t* s); +static void _destroy_ref(_ref_t* ref); + +static void _gc_add(_ref_t* ref, void* data, _gc_t* gc); +static void _gc_remove(_ref_t* ref, void* data, _gc_t* gc); +static int _gc_add_reachable(void* data, void* extra, void* h); +static int _gc_add_reachable_both(void* data, void* extra, void* h); +static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht); +static int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc); +static int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc); +#ifdef MB_ENABLE_CLASS +static int _gc_destroy_garbage_in_class(void* data, void* extra, _gc_t* gc); +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_LAMBDA +static int _gc_destroy_garbage_in_lambda(void* data, void* extra, _gc_t* gc); +static void _gc_destroy_garbage_in_outer_scope(_running_context_ref_t* p, _gc_t* gc); +#endif /* MB_ENABLE_LAMBDA */ +static int _gc_destroy_garbage(void* data, void* extra, _gc_t* gc); +#ifdef MB_ENABLE_CLASS +static int _gc_destroy_garbage_class(void* data, void* extra, _gc_t* gc); +#endif /* MB_ENABLE_CLASS */ +static void _gc_swap_tables(mb_interpreter_t* s); +static void _gc_try_trigger(mb_interpreter_t* s); +static void _gc_collect_garbage(mb_interpreter_t* s, int depth); + +#ifdef MB_ENABLE_USERTYPE_REF +static _usertype_ref_t* _create_usertype_ref(mb_interpreter_t* s, void* val, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft); +static void _destroy_usertype_ref(_usertype_ref_t* c); +static void _unref_usertype_ref(_ref_t* ref, void* data); +#endif /* MB_ENABLE_USERTYPE_REF */ + +static _array_t* _create_array(mb_interpreter_t* s, const char* n, _data_e t); +#ifdef MB_ENABLE_ARRAY_REF +static void _destroy_array(_array_t* arr); +#endif /* MB_ENABLE_ARRAY_REF */ +static void _init_array(_array_t* arr); +static _array_t* _clone_array(mb_interpreter_t* s, _array_t* arr); +static int _get_array_pos(mb_interpreter_t* s, _array_t* arr, int* d, int c); +static int _get_array_index(mb_interpreter_t* s, _ls_node_t** l, _object_t* c, unsigned* index, bool_t* literally); +static bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type); +static int _set_array_elem(mb_interpreter_t* s, _ls_node_t* ast, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type); +static void _clear_array(_array_t* arr); +static bool_t _is_array(void* obj); +#ifdef MB_ENABLE_ARRAY_REF +static void _unref_array(_ref_t* ref, void* data); +#endif /* MB_ENABLE_ARRAY_REF */ + +#ifdef MB_ENABLE_COLLECTION_LIB +static _list_t* _create_list(mb_interpreter_t* s); +static void _destroy_list(_list_t* c); +static _dict_t* _create_dict(mb_interpreter_t* s); +static void _destroy_dict(_dict_t* c); +static _list_it_t* _create_list_it(_list_t* coll, bool_t lock); +static bool_t _destroy_list_it(_list_it_t* it); +static _list_it_t* _move_list_it_next(_list_it_t* it); +static _dict_it_t* _create_dict_it(_dict_t* coll, bool_t lock); +static bool_t _destroy_dict_it(_dict_it_t* it); +static _dict_it_t* _move_dict_it_next(_dict_it_t* it); +static void _unref_list(_ref_t* ref, void* data); +static void _unref_dict(_ref_t* ref, void* data); +static void _push_list(_list_t* coll, mb_value_t* val, _object_t* oarg); +static bool_t _pop_list(_list_t* coll, mb_value_t* val, mb_interpreter_t* s); +static bool_t _insert_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval); +static bool_t _set_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval); +static bool_t _remove_at_list(_list_t* coll, int_t idx); +static _ls_node_t* _node_at_list(_list_t* coll, int index); +static bool_t _at_list(_list_t* coll, int_t idx, mb_value_t* oval); +static bool_t _find_list(_list_t* coll, mb_value_t* val, int* idx); +static void _clear_list(_list_t* coll); +static void _sort_list(_list_t* coll); +static void _invalidate_list_cache(_list_t* coll); +static void _fill_ranged(_list_t* coll); +static void _set_dict(_dict_t* coll, mb_value_t* key, mb_value_t* val, _object_t* okey, _object_t* oval); +static bool_t _remove_dict(_dict_t* coll, mb_value_t* key); +static bool_t _find_dict(_dict_t* coll, mb_value_t* val, mb_value_t* oval); +static void _clear_dict(_dict_t* coll); +static bool_t _invalid_list_it(_list_it_t* it); +static bool_t _invalid_dict_it(_dict_it_t* it); +static bool_t _assign_with_it(_object_t* tgt, _object_t* src); +static int _clone_to_list(void* data, void* extra, _list_t* coll); +static int _clone_to_dict(void* data, void* extra, _dict_t* coll); +static int _copy_list_to_array(void* data, void* extra, _array_helper_t* h); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifdef MB_ENABLE_CLASS +typedef int (* _class_scope_walker)(void*, void*, void*); +typedef bool_t (* _class_meta_walker)(_class_t*, void*, void*); +static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n); +static void _begin_class(mb_interpreter_t* s); +static bool_t _end_class(mb_interpreter_t* s); +static void _unref_class(_ref_t* ref, void* data); +static void _destroy_class(_class_t* c); +static bool_t _traverse_class(_class_t* c, _class_scope_walker scope_walker, _class_meta_walker meta_walker, unsigned meta_depth, bool_t meta_walk_on_self, void* extra_data, void* ret); +static bool_t _link_meta_class(mb_interpreter_t* s, _class_t* derived, _class_t* base); +static void _unlink_meta_class(mb_interpreter_t* s, _class_t* derived); +static int _unlink_meta_instance(void* data, void* extra, _class_t* derived); +static int _clone_clsss_field(void* data, void* extra, void* n); +static bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret); +static int _search_class_meta_function(mb_interpreter_t* s, _class_t* instance, const char* n, _routine_t* _mb_unaligned * f); +static int _search_class_hash_and_compare_functions(mb_interpreter_t* s, _class_t* instance); +static bool_t _is_a_class(_class_t* instance, void* m, void* ret); +static bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret); +#ifdef MB_ENABLE_COLLECTION_LIB +static int _reflect_class_field(void* data, void* extra, void* d); +#endif /* MB_ENABLE_COLLECTION_LIB */ +static int _format_class_to_string(mb_interpreter_t* s, void** l, _class_t* instance, _object_t* out, bool_t* got_tostr); +static _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg); +static bool_t _is_valid_class_accessor_following_routine(mb_interpreter_t* s, _var_t* var, _ls_node_t* ast, _ls_node_t** out); +#endif /* MB_ENABLE_CLASS */ +static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f); +static int _begin_routine(mb_interpreter_t* s); +static bool_t _end_routine(mb_interpreter_t* s); +static void _begin_routine_parameter_list(mb_interpreter_t* s); +static void _end_routine_parameter_list(mb_interpreter_t* s); +static _object_t* _duplicate_parameter(void* data, void* extra, _running_context_t* running); +static _routine_t* _clone_routine(_routine_t* sub, void* c, bool_t toupval); +#ifdef MB_ENABLE_LAMBDA +static _running_context_t* _init_lambda(mb_interpreter_t* s, _routine_t* routine); +static void _unref_routine(_ref_t* ref, void* data); +static void _destroy_routine(mb_interpreter_t* s, _routine_t* r); +static void _mark_upvalue(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj, const char* n); +static void _try_mark_upvalue(mb_interpreter_t* s, _routine_t* r, _object_t* obj); +static _running_context_ref_t* _create_outer_scope(mb_interpreter_t* s); +static void _unref_outer_scope(_ref_t* ref, void* data); +static void _destroy_outer_scope(_running_context_ref_t* p); +static int _do_nothing_on_ht_for_lambda(void* data, void* extra); +static int _fill_with_upvalue(void* data, void* extra, _upvalue_scope_tuple_t* tuple); +static int _remove_filled_upvalue(void* data, void* extra, _ht_node_t* ht); +static int _fill_outer_scope(void* data, void* extra, _upvalue_scope_tuple_t* tuple); +static _running_context_t* _link_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak); +static _running_context_t* _unlink_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak); +static bool_t _is_valid_lambda_body_node(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj); +#endif /* MB_ENABLE_LAMBDA */ +#ifdef MB_ENABLE_CLASS +static _running_context_t* _reference_scope_by_class(mb_interpreter_t* s, _running_context_t* p, _class_t* c); +static _running_context_t* _push_scope_by_class(mb_interpreter_t* s, _running_context_t* p); +static _ls_node_t* _search_identifier_in_class(mb_interpreter_t* s, _class_t* instance, const char* n, _ht_node_t** ht, _running_context_t** sp); +#endif /* MB_ENABLE_CLASS */ +static _running_context_t* _reference_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r); +static _running_context_t* _push_weak_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r); +static _running_context_t* _push_scope_by_routine(mb_interpreter_t* s, _running_context_t* p); +static void _destroy_scope(mb_interpreter_t* s, _running_context_t* p); +static _running_context_t* _pop_weak_scope(mb_interpreter_t* s, _running_context_t* p); +static _running_context_t* _pop_scope(mb_interpreter_t* s, bool_t tidy); +static void _out_of_scope(mb_interpreter_t* s, _running_context_t* running, void* instance, _routine_t* routine, bool_t lose); +static _running_context_t* _find_scope(mb_interpreter_t* s, _running_context_t* p); +static _running_context_t* _get_root_scope(_running_context_t* scope); +#ifdef MB_ENABLE_LAMBDA +static _running_context_ref_t* _get_root_ref_scope(_running_context_ref_t* scope); +#endif /* MB_ENABLE_LAMBDA */ +static _running_context_t* _get_scope_to_add_routine(mb_interpreter_t* s); +static _ls_node_t* _search_identifier_in_scope_chain(mb_interpreter_t* s, _running_context_t* scope, const char* n, int pathing, _ht_node_t** ht, _running_context_t** sp); +static _array_t* _search_array_in_scope_chain(mb_interpreter_t* s, _array_t* i, _object_t** o); +static _var_t* _search_var_in_scope_chain(mb_interpreter_t* s, _var_t* i); +static _ls_node_t* _search_identifier_accessor(mb_interpreter_t* s, _running_context_t* scope, const char* n, _ht_node_t** ht, _running_context_t** sp, bool_t unknown_for_not_found); + +static _var_t* _create_var(_object_t** oobj, const char* n, size_t ns, bool_t dup_name); +static _object_t* _create_object(void); +static int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt, bool_t toupval, bool_t deep); +static int _dispose_object(_object_t* obj); +static int _destroy_object(void* data, void* extra); +static int _destroy_object_with_extra(void* data, void* extra); +static int _destroy_object_not_compile_time(void* data, void* extra); +static int _destroy_object_capsule_only(void* data, void* extra); +static int _do_nothing_on_object(void* data, void* extra); +static int _lose_object(void* data, void* extra); +static int _remove_source_object(void* data, void* extra); +static int _destroy_memory(void* data, void* extra); +static int _compare_numbers(const _object_t* first, const _object_t* second); +static bool_t _is_internal_object(_object_t* obj); +static _data_e _public_type_to_internal_type(mb_data_e t); +static mb_data_e _internal_type_to_public_type(_data_e t); +static int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn); +static int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl); +static int _create_internal_object_from_public_value(mb_value_t* pbl, _object_t** itn); +static int _compare_public_value_and_internal_object(mb_value_t* pbl, _object_t* itn); +static void _try_clear_intermediate_value(void* data, void* extra, mb_interpreter_t* s); +static void _remove_if_exist(void* data, void* extra, _ls_node_t* ls); +static void _destroy_var_arg(void* data, void* extra, _gc_t* gc); +static void _destroy_edge_objects(mb_interpreter_t* s); +static void _mark_edge_destroy_string(mb_interpreter_t* s, char* ch); +static void _destroy_lazy_objects(mb_interpreter_t* s); +static void _mark_lazy_destroy_string(mb_interpreter_t* s, char* ch); +static void _assign_public_value(mb_value_t* tgt, mb_value_t* src); +static void _swap_public_value(mb_value_t* tgt, mb_value_t* src); +static int _clear_scope_chain(mb_interpreter_t* s); +static int _dispose_scope_chain(mb_interpreter_t* s); +static void _tidy_scope_chain(mb_interpreter_t* s); +static void _tidy_intermediate_value(_ref_t* ref, void* data); +static _object_t* _eval_var_in_print(mb_interpreter_t* s, _object_t** val_ptr, _ls_node_t** ast, _object_t* obj); + +/** Interpretation */ + +static void _stepped(mb_interpreter_t* s, _ls_node_t* ast); +static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l, bool_t force_next); +static int _common_end_looping(mb_interpreter_t* s, _ls_node_t** l); +static int _common_keep_looping(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); +static int _execute_normal_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); +#ifdef MB_ENABLE_COLLECTION_LIB +static int _execute_ranged_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop); +#endif /* MB_ENABLE_COLLECTION_LIB */ +static int _skip_to(mb_interpreter_t* s, _ls_node_t** l, mb_func_t f, _data_e t); +static int _skip_if_chunk(mb_interpreter_t* s, _ls_node_t** l); +static int _skip_struct(mb_interpreter_t* s, _ls_node_t** l, mb_func_t open_func, mb_func_t close_func); + +static _running_context_t* _create_running_context(bool_t create_var_dict); +static _parsing_context_t* _reset_parsing_context(_parsing_context_t* context); +static void _destroy_parsing_context(_parsing_context_t* _mb_unaligned * context); + +/** Interface processors */ + +#ifdef MB_ENABLE_MODULE +static _module_func_t* _create_module_func(mb_interpreter_t* s, mb_func_t f); +static int _ls_destroy_module_func(void* data, void* extra); +static int _ht_destroy_module_func_list(void* data, void* extra); +#endif /* MB_ENABLE_MODULE */ +static char* _generate_func_name(mb_interpreter_t* s, char* n, bool_t with_mod); +static int _register_func(mb_interpreter_t* s, char* n, mb_func_t f, bool_t local); +static int _remove_func(mb_interpreter_t* s, char* n, bool_t local); +static _ls_node_t* _find_func(mb_interpreter_t* s, char* n, bool_t* mod); + +static int _open_constant(mb_interpreter_t* s); +static int _close_constant(mb_interpreter_t* s); +static int _open_core_lib(mb_interpreter_t* s); +static int _close_core_lib(mb_interpreter_t* s); +static int _open_std_lib(mb_interpreter_t* s); +static int _close_std_lib(mb_interpreter_t* s); +#ifdef MB_ENABLE_COLLECTION_LIB +static int _open_coll_lib(mb_interpreter_t* s); +static int _close_coll_lib(mb_interpreter_t* s); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* ========================================================} */ + +/* +** {======================================================== +** Lib declarations +*/ + +/** Macro */ + +#ifdef MB_CP_VC +# if MB_CP_VC < 1300 +# define MB_FUNC 0 +# else /* MB_CP_VC < 1300 */ +# define MB_FUNC __FUNCTION__ +# endif /* MB_CP_VC < 1300 */ +#elif defined MB_CP_BORLANDC +# define MB_FUNC __FUNC__ +#elif defined MB_CP_PELLESC +# define MB_FUNC __func__ +#else /* MB_CP_VC */ +# define MB_FUNC __FUNCTION__ +#endif /* MB_CP_VC */ + +#ifdef MB_CP_VC +# if MB_CP_VC < 1300 +# define _do_nothing(__s, __l, __exit, __result) \ + do { \ + _ls_node_t* ast = 0; static int i = 0; ++i; \ + printf("Unaccessable function called %d times.\n", i); \ + ast = (_ls_node_t*)(*(__l)); \ + _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (__s)->source_file, DON(ast), MB_FUNC_ERR, __exit, (__result)); \ + } while(0) +# endif /* MB_CP_VC < 1300 */ +#endif /* MB_CP_VC */ +#ifndef _do_nothing +# define _do_nothing(__s, __l, __exit, __result) \ + do { \ + _ls_node_t* ast = (_ls_node_t*)(*(__l)); \ + _handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (char*)MB_FUNC, DON(ast), MB_FUNC_ERR, __exit, (__result)); \ + } while(0); +#endif /* _do_nothing */ + +/** Core lib */ + +static int _core_dummy_assign(mb_interpreter_t* s, void** l); +static int _core_add(mb_interpreter_t* s, void** l); +static int _core_min(mb_interpreter_t* s, void** l); +static int _core_mul(mb_interpreter_t* s, void** l); +static int _core_div(mb_interpreter_t* s, void** l); +static int _core_mod(mb_interpreter_t* s, void** l); +static int _core_pow(mb_interpreter_t* s, void** l); +static int _core_open_bracket(mb_interpreter_t* s, void** l); +static int _core_close_bracket(mb_interpreter_t* s, void** l); +static int _core_neg(mb_interpreter_t* s, void** l); +static int _core_equal(mb_interpreter_t* s, void** l); +static int _core_less(mb_interpreter_t* s, void** l); +static int _core_greater(mb_interpreter_t* s, void** l); +static int _core_less_equal(mb_interpreter_t* s, void** l); +static int _core_greater_equal(mb_interpreter_t* s, void** l); +static int _core_not_equal(mb_interpreter_t* s, void** l); +static int _core_and(mb_interpreter_t* s, void** l); +static int _core_or(mb_interpreter_t* s, void** l); +static int _core_not(mb_interpreter_t* s, void** l); +static int _core_is(mb_interpreter_t* s, void** l); +static int _core_let(mb_interpreter_t* s, void** l); +static int _core_dim(mb_interpreter_t* s, void** l); +static int _core_if(mb_interpreter_t* s, void** l); +static int _core_then(mb_interpreter_t* s, void** l); +static int _core_elseif(mb_interpreter_t* s, void** l); +static int _core_else(mb_interpreter_t* s, void** l); +static int _core_endif(mb_interpreter_t* s, void** l); +static int _core_for(mb_interpreter_t* s, void** l); +#ifdef MB_ENABLE_COLLECTION_LIB +static int _core_in(mb_interpreter_t* s, void** l); +#endif /* MB_ENABLE_COLLECTION_LIB */ +static int _core_to(mb_interpreter_t* s, void** l); +static int _core_step(mb_interpreter_t* s, void** l); +static int _core_next(mb_interpreter_t* s, void** l); +static int _core_while(mb_interpreter_t* s, void** l); +static int _core_wend(mb_interpreter_t* s, void** l); +static int _core_do(mb_interpreter_t* s, void** l); +static int _core_until(mb_interpreter_t* s, void** l); +static int _core_exit(mb_interpreter_t* s, void** l); +static int _core_goto(mb_interpreter_t* s, void** l); +static int _core_gosub(mb_interpreter_t* s, void** l); +static int _core_return(mb_interpreter_t* s, void** l); +static int _core_call(mb_interpreter_t* s, void** l); +static int _core_def(mb_interpreter_t* s, void** l); +static int _core_enddef(mb_interpreter_t* s, void** l); +static int _core_args(mb_interpreter_t* s, void** l); +#ifdef MB_ENABLE_CLASS +static int _core_class(mb_interpreter_t* s, void** l); +static int _core_endclass(mb_interpreter_t* s, void** l); +static int _core_new(mb_interpreter_t* s, void** l); +static int _core_var(mb_interpreter_t* s, void** l); +static int _core_reflect(mb_interpreter_t* s, void** l); +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_LAMBDA +static int _core_lambda(mb_interpreter_t* s, void** l); +#endif /* MB_ENABLE_LAMBDA */ +#ifdef MB_ENABLE_ALLOC_STAT +static int _core_mem(mb_interpreter_t* s, void** l); +#endif /* MB_ENABLE_ALLOC_STAT */ +static int _core_type(mb_interpreter_t* s, void** l); +static int _core_import(mb_interpreter_t* s, void** l); +static int _core_end(mb_interpreter_t* s, void** l); + +/** Standard lib */ + +static int _std_abs(mb_interpreter_t* s, void** l); +static int _std_sgn(mb_interpreter_t* s, void** l); +static int _std_sqr(mb_interpreter_t* s, void** l); +static int _std_floor(mb_interpreter_t* s, void** l); +static int _std_ceil(mb_interpreter_t* s, void** l); +static int _std_fix(mb_interpreter_t* s, void** l); +static int _std_round(mb_interpreter_t* s, void** l); +static int _std_srnd(mb_interpreter_t* s, void** l); +static int _std_rnd(mb_interpreter_t* s, void** l); +static int _std_sin(mb_interpreter_t* s, void** l); +static int _std_cos(mb_interpreter_t* s, void** l); +static int _std_tan(mb_interpreter_t* s, void** l); +static int _std_asin(mb_interpreter_t* s, void** l); +static int _std_acos(mb_interpreter_t* s, void** l); +static int _std_atan(mb_interpreter_t* s, void** l); +static int _std_exp(mb_interpreter_t* s, void** l); +static int _std_log(mb_interpreter_t* s, void** l); +static int _std_asc(mb_interpreter_t* s, void** l); +static int _std_chr(mb_interpreter_t* s, void** l); +static int _std_left(mb_interpreter_t* s, void** l); +static int _std_mid(mb_interpreter_t* s, void** l); +static int _std_right(mb_interpreter_t* s, void** l); +static int _std_str(mb_interpreter_t* s, void** l); +static int _std_val(mb_interpreter_t* s, void** l); +static int _std_len(mb_interpreter_t* s, void** l); +static int _std_get(mb_interpreter_t* s, void** l); +static int _std_set(mb_interpreter_t* s, void** l); +static int _std_print(mb_interpreter_t* s, void** l); +static int _std_input(mb_interpreter_t* s, void** l); + +/** Collection lib */ + +#ifdef MB_ENABLE_COLLECTION_LIB +static int _coll_list(mb_interpreter_t* s, void** l); +static int _coll_dict(mb_interpreter_t* s, void** l); +static int _coll_push(mb_interpreter_t* s, void** l); +static int _coll_pop(mb_interpreter_t* s, void** l); +static int _coll_peek(mb_interpreter_t* s, void** l); +static int _coll_insert(mb_interpreter_t* s, void** l); +static int _coll_sort(mb_interpreter_t* s, void** l); +static int _coll_exist(mb_interpreter_t* s, void** l); +static int _coll_index_of(mb_interpreter_t* s, void** l); +static int _coll_remove(mb_interpreter_t* s, void** l); +static int _coll_clear(mb_interpreter_t* s, void** l); +static int _coll_clone(mb_interpreter_t* s, void** l); +static int _coll_to_array(mb_interpreter_t* s, void** l); +static int _coll_iterator(mb_interpreter_t* s, void** l); +static int _coll_move_next(mb_interpreter_t* s, void** l); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/** Lib information */ + +#define _CORE_ID_TYPE "TYPE" + +static const _func_t _core_libs[] = { + { _DUMMY_ASSIGN_CHAR, _core_dummy_assign }, + { "+", _core_add }, + { "-", _core_min }, + { "*", _core_mul }, + { "/", _core_div }, + { "MOD", _core_mod }, + { "^", _core_pow }, + { "(", _core_open_bracket }, + { ")", _core_close_bracket }, + { 0, _core_neg }, + + { "=", _core_equal }, + { "<", _core_less }, + { ">", _core_greater }, + { "<=", _core_less_equal }, + { ">=", _core_greater_equal }, + { "<>", _core_not_equal }, + + { "AND", _core_and }, + { "OR", _core_or }, + { "NOT", _core_not }, + + { "IS", _core_is }, + + { "LET", _core_let }, + { "DIM", _core_dim }, + + { "IF", _core_if }, + { "THEN", _core_then }, + { "ELSEIF", _core_elseif }, + { "ELSE", _core_else }, + { "ENDIF", _core_endif }, + + { "FOR", _core_for }, +#ifdef MB_ENABLE_COLLECTION_LIB + { "IN", _core_in }, +#endif /* MB_ENABLE_COLLECTION_LIB */ + { "TO", _core_to }, + { "STEP", _core_step }, + { "NEXT", _core_next }, + { "WHILE", _core_while }, + { "WEND", _core_wend }, + { "DO", _core_do }, + { "UNTIL", _core_until }, + + { "EXIT", _core_exit }, + { "GOTO", _core_goto }, + { "GOSUB", _core_gosub }, + { "RETURN", _core_return }, + + { "CALL", _core_call }, + { "DEF", _core_def }, + { "ENDDEF", _core_enddef }, + { _VAR_ARGS_STR, _core_args }, + +#ifdef MB_ENABLE_CLASS + { "CLASS", _core_class }, + { "ENDCLASS", _core_endclass }, + { "NEW", _core_new }, + { "VAR", _core_var }, + { "REFLECT", _core_reflect }, +#endif /* MB_ENABLE_CLASS */ + +#ifdef MB_ENABLE_LAMBDA + { "LAMBDA", _core_lambda }, +#endif /* MB_ENABLE_LAMBDA */ + +#ifdef MB_ENABLE_ALLOC_STAT + { "MEM", _core_mem }, +#endif /* MB_ENABLE_ALLOC_STAT */ + + { _CORE_ID_TYPE, _core_type }, + { "IMPORT", _core_import }, + { "END", _core_end } +}; + +#define _STD_ID_VAL "VAL" +#define _STD_ID_LEN "LEN" +#define _STD_ID_GET "GET" +#define _STD_ID_SET "SET" + +static const _func_t _std_libs[] = { + { "ABS", _std_abs }, + { "SGN", _std_sgn }, + { "SQR", _std_sqr }, + { "FLOOR", _std_floor }, + { "CEIL", _std_ceil }, + { "FIX", _std_fix }, + { "ROUND", _std_round }, + { "SRND", _std_srnd }, + { "RND", _std_rnd }, + { "SIN", _std_sin }, + { "COS", _std_cos }, + { "TAN", _std_tan }, + { "ASIN", _std_asin }, + { "ACOS", _std_acos }, + { "ATAN", _std_atan }, + { "EXP", _std_exp }, + { "LOG", _std_log }, + + { "ASC", _std_asc }, + { "CHR", _std_chr }, + { "LEFT", _std_left }, + { "MID", _std_mid }, + { "RIGHT", _std_right }, + { "STR", _std_str }, + { _STD_ID_VAL, _std_val }, + + { _STD_ID_LEN, _std_len }, + { _STD_ID_GET, _std_get }, + { _STD_ID_SET, _std_set }, + + { "PRINT", _std_print }, + { "INPUT", _std_input } +}; + +#ifdef MB_ENABLE_COLLECTION_LIB +# define _COLL_ID_LIST "LIST" +# define _COLL_ID_DICT "DICT" +# define _COLL_ID_PUSH "PUSH" +# define _COLL_ID_POP "POP" +# define _COLL_ID_PEEK "PEEK" +# define _COLL_ID_INSERT "INSERT" +# define _COLL_ID_SORT "SORT" +# define _COLL_ID_EXIST "EXIST" +# define _COLL_ID_INDEX_OF "INDEX_OF" +# define _COLL_ID_REMOVE "REMOVE" +# define _COLL_ID_CLEAR "CLEAR" +# define _COLL_ID_CLONE "CLONE" +# define _COLL_ID_TO_ARRAY "TO_ARRAY" +# define _COLL_ID_ITERATOR "ITERATOR" +# define _COLL_ID_MOVE_NEXT "MOVE_NEXT" + +static const _func_t _coll_libs[] = { + { _COLL_ID_LIST, _coll_list }, + { _COLL_ID_DICT, _coll_dict }, + { _COLL_ID_PUSH, _coll_push }, + { _COLL_ID_POP, _coll_pop }, + { _COLL_ID_PEEK, _coll_peek }, + { _COLL_ID_INSERT, _coll_insert }, + { _COLL_ID_SORT, _coll_sort }, + { _COLL_ID_EXIST, _coll_exist }, + { _COLL_ID_INDEX_OF, _coll_index_of }, + { _COLL_ID_REMOVE, _coll_remove }, + { _COLL_ID_CLEAR, _coll_clear }, + { _COLL_ID_CLONE, _coll_clone }, + { _COLL_ID_TO_ARRAY, _coll_to_array }, + { _COLL_ID_ITERATOR, _coll_iterator }, + { _COLL_ID_MOVE_NEXT, _coll_move_next } +}; +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* ========================================================} */ + +/* +** {======================================================== +** Private function definitions +*/ + +/** List operations */ + +static int _ls_cmp_data(void* node, void* info) { + _ls_node_t* n = (_ls_node_t*)node; + + return (n->data == info) ? 0 : 1; +} + +static int _ls_cmp_extra(void* node, void* info) { + _ls_node_t* n = (_ls_node_t*)node; + + return (n->extra == info) ? 0 : 1; +} + +static int _ls_cmp_extra_object(void* node, void* info) { + _ls_node_t* n = (_ls_node_t*)node; + + return _ht_cmp_object(n->extra, info); +} + +static int _ls_cmp_extra_string(void* node, void* info) { + _ls_node_t* n = (_ls_node_t*)node; + char* s1 = (char*)n->extra; + char* s2 = (char*)info; + + return strcmp(s1, s2); +} + +#ifdef MB_ENABLE_MODULE +static int _ls_cmp_module_func(void* node, void* info) { + _module_func_t* m = (_module_func_t*)node; + mb_interpreter_t* s = (mb_interpreter_t*)info; + + return strcmp(m->module, s->with_module); +} +#endif /* MB_ENABLE_MODULE */ + +static _ls_node_t* _ls_create_node(void* data) { + _ls_node_t* result = 0; + + result = (_ls_node_t*)mb_malloc(sizeof(_ls_node_t)); + mb_assert(result); + memset(result, 0, sizeof(_ls_node_t)); + result->data = data; + + return result; +} + +static _ls_node_t* _ls_create(void) { + _ls_node_t* result = 0; + + result = _ls_create_node(0); + + return result; +} + +static _ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp, int* idx) { + _ls_node_t* result = 0; + + mb_assert(list && data && cmp); + + if(idx) *idx = 0; + + list = list->next; + while(list) { + if(!cmp(list->data, data)) { + result = list; + + break; + } + list = list->next; + if(idx) ++*idx; + } + + return result; +} + +static _ls_node_t* _ls_back(_ls_node_t* node) { + _ls_node_t* result = node; + + result = result->prev; + + return result; +} + +static _ls_node_t* _ls_pushback(_ls_node_t* list, void* data) { + _ls_node_t* result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list); + + result = _ls_create_node(data); + + tmp = _ls_back(list); + if(!tmp) + tmp = list; + tmp->next = result; + result->prev = tmp; + list->prev = result; + list->data = (char*)list->data + 1; + + return result; +} + +static void* _ls_popback(_ls_node_t* list) { + void* result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list); + + tmp = _ls_back(list); + if(tmp) { + result = tmp->data; + if(list != tmp->prev) + list->prev = tmp->prev; + else + list->prev = 0; + tmp->prev->next = 0; + safe_free(tmp); + list->data = (char*)list->data - 1; + } + + return result; +} + +static _ls_node_t* _ls_front(_ls_node_t* node) { + _ls_node_t* result = node; + + result = result->next; + + return result; +} + +static void* _ls_popfront(_ls_node_t* list) { + void* result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list); + + tmp = _ls_front(list); + if(tmp) { + result = tmp->data; + list->next = tmp->next; + if(tmp->next) + tmp->next->prev = list; + if(!list->next) + list->prev = 0; + tmp->prev = tmp->next = 0; + safe_free(tmp); + list->data = (char*)list->data - 1; + } + + return result; +} + +static _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data) { + _ls_node_t* result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list); + + tmp = list->next; + while(tmp && index) { + tmp = tmp->next; + --index; + } + if(!tmp) { + if(index == 0) + result = _ls_pushback(list, data); + } else { + result = _ls_create_node(data); + tmp->prev->next = result; + result->prev = tmp->prev; + result->next = tmp; + tmp->prev = result; + } + list->data = (char*)list->data + 1; + + return result; +} + +static unsigned _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op) { + unsigned result = 0; + + mb_assert(list && node); + + if(node->prev) + node->prev->next = node->next; + if(node->next) + node->next->prev = node->prev; + if(list->prev == node) + list->prev = node->prev; + if(list->prev == list) + list->prev = 0; + if(op) + op(node->data, node->extra); + safe_free(node); + ++result; + list->data = (char*)list->data - 1; + + return result; +} + +static unsigned _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op) { + unsigned result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list && cmp); + + tmp = list->next; + while(tmp) { + if(cmp(tmp, info) == 0) { + if(tmp->prev) + tmp->prev->next = tmp->next; + if(tmp->next) + tmp->next->prev = tmp->prev; + if(list->prev == tmp) + list->prev = tmp->prev; + if(list->prev == list) + list->prev = 0; + if(op) + op(tmp->data, tmp->extra); + safe_free(tmp); + ++result; + list->data = (char*)list->data - 1; + + break; + } + tmp = tmp->next; + } + + return result; +} + +static unsigned _ls_foreach(_ls_node_t* list, _ls_operation op) { + unsigned idx = 0; + int opresult = _OP_RESULT_NORMAL; + _ls_node_t* node = 0; + _ls_node_t* tmp = 0; + + mb_assert(list && op); + + node = list->next; + while(node) { + opresult = op(node->data, node->extra); + ++idx; + tmp = node; + node = node->next; + + if(_OP_RESULT_DEL_NODE == opresult) { + tmp->prev->next = node; + if(node) + node->prev = tmp->prev; + safe_free(tmp); + list->data = (char*)list->data - 1; + } + } + + return idx; +} + +static _ls_node_t* _ls_sort(_ls_node_t* _mb_unaligned * list, _ls_compare cmp) { + /* Copyright 2001 Simon Tatham, http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c */ + bool_t is_circular = false, is_double = true; + _ls_node_t* p, * q, * e, * tail, * oldhead; + int insize, nmerges, psize, qsize, i; + _ls_node_t* lst = 0; + + mb_assert(list && *list && cmp); + + lst = *list; + if(lst) lst = lst->next; + + if(!lst) + return 0; + + insize = 1; + + while(1) { + p = lst; + oldhead = lst; + lst = 0; + tail = 0; + + nmerges = 0; + + while(p) { + nmerges++; + q = p; + psize = 0; + for(i = 0; i < insize; i++) { + psize++; + if(is_circular) + q = (q->next == oldhead ? 0 : q->next); + else + q = q->next; + if(!q) + break; + } + + qsize = insize; + + while(psize > 0 || (qsize > 0 && q)) { + if(psize == 0) { + e = q; q = q->next; qsize--; + if(is_circular && q == oldhead) q = 0; + } else if(qsize == 0 || !q) { + e = p; p = p->next; psize--; + if(is_circular && p == oldhead) p = 0; + } else if(cmp(p->data, q->data) <= 0) { + e = p; p = p->next; psize--; + if(is_circular && p == oldhead) p = 0; + } else { + e = q; q = q->next; qsize--; + if(is_circular && q == oldhead) q = 0; + } + + if(tail) + tail->next = e; + else + lst = e; + if(is_double) + e->prev = tail; + tail = e; + } + + p = q; + } + if(is_circular) { + tail->next = lst; + if(is_double) + lst->prev = tail; + } else { + tail->next = 0; + } + + if(nmerges <= 1) { + (*list)->next = lst; + (*list)->prev = tail; + + lst->prev = *list; + + return *list; + } + + insize *= 2; + } +} + +static unsigned _ls_count(_ls_node_t* list) { + union { void* p; unsigned u; } tmp; + + mb_assert(list); + + tmp.p = list->data; + + return tmp.u; +} + +static bool_t _ls_empty(_ls_node_t* list) { + bool_t result = false; + + mb_assert(list); + + result = 0 == list->next; + + return result; +} + +static void _ls_clear(_ls_node_t* list) { + _ls_node_t* tmp = 0; + + mb_assert(list); + + list->data = 0; + + tmp = list; + list = list->next; + tmp->next = 0; + tmp->prev = 0; + + while(list) { + tmp = list; + list = list->next; + safe_free(tmp); + } +} + +static void _ls_destroy(_ls_node_t* list) { + _ls_clear(list); + safe_free(list); +} + +static int _ls_free_extra(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + mb_unrefvar(data); + + mb_assert(extra); + + safe_free(extra); + + return result; +} + +/** Dictionary operations */ + +static unsigned _ht_hash_object(void* ht, void* d) { + unsigned result = 0; + _ht_node_t* self = (_ht_node_t*)ht; + _object_t* o = (_object_t*)d; + size_t i = 0; + unsigned h = 0; +#ifdef MB_ENABLE_CLASS + _object_t val; +#endif /* MB_ENABLE_CLASS */ + + mb_assert(ht); + + h = o->type; + switch(o->type) { + case _DT_STRING: + h = 5 * h + _ht_hash_string(ht, o->data.string); + if(self->array_size == 1) + result = 0; + else + result = h % self->array_size; + + break; +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + if(o->data.instance->hash) { + mb_interpreter_t* s = o->data.instance->ref.s; + _ls_node_t ast; + _ls_node_t* tmp = * + mb_value_t va[1]; + mb_make_nil(va[0]); + memset(&ast, 0, sizeof(_ls_node_t)); + if(_eval_routine(s, &tmp, va, 1, o->data.instance->hash, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { + _MAKE_NIL(&val); + _public_value_to_internal_object(&s->running_context->intermediate_value, &val); + if(val.type != _DT_INT) { + int ignored = MB_FUNC_OK; + _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, o, MB_FUNC_ERR, _exit, ignored); + } + + o = &val; + } + } + + goto _default; +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + if(o->data.usertype_ref->hash) { + if(self->array_size == 1) { + result = 0; + } else { + h = 5 * h + o->data.usertype_ref->hash(o->data.usertype_ref->ref.s, o->data.usertype_ref->usertype); + result = h % self->array_size; + } + + break; + } + + goto _default; +#endif /* MB_ENABLE_USERTYPE_REF */ + default: +#if defined MB_ENABLE_CLASS || defined MB_ENABLE_USERTYPE_REF +_default: +#endif /* MB_ENABLE_CLASS || MB_ENABLE_USERTYPE_REF */ + if(self->array_size == 1) { + result = 0; + } else { + for(i = 0; i < sizeof(_raw_t); ++i) + h = 5 * h + o->data.raw[i]; + result = h % self->array_size; + } + + break; + } + + goto _exit; /* Avoid an unreferenced label warning */ + +_exit: + return result; +} + +static unsigned _ht_hash_string(void* ht, void* d) { + unsigned result = 0; + _ht_node_t* self = (_ht_node_t*)ht; + char* s = (char*)d; + unsigned h = 0; + + mb_assert(ht); + + if(self->array_size == 1) { + result = 0; + } else { + for( ; *s; ++s) + h = 5 * h + *s; + result = h % self->array_size; + } + + return result; +} + +static unsigned _ht_hash_intptr(void* ht, void* d) { + unsigned result = 0; + _ht_node_t* self = (_ht_node_t*)ht; + intptr_t i = *(intptr_t*)d; + + mb_assert(ht); + + if(self->array_size == 1) + result = 0; + else + result = (unsigned)(i % self->array_size); + + return result; +} + +static unsigned _ht_hash_ref(void* ht, void* d) { + unsigned result = 0; + _ht_node_t* self = (_ht_node_t*)ht; + _ref_t* ref = (_ref_t*)d; + + mb_assert(ht); + + if(self->array_size == 1) { + result = 0; + } else { + result = (unsigned)(intptr_t)ref; + result %= self->array_size; + } + + return result; +} + +static int _ht_cmp_object(void* d1, void* d2) { + _object_t* o1 = (_object_t*)d1; + _object_t* o2 = (_object_t*)d2; + int i = 0; +#ifdef MB_ENABLE_CLASS + _routine_t* cmp = 0; + _object_t val; + bool_t fst = true; +#endif /* MB_ENABLE_CLASS */ + + if(o1->type < o2->type) + return -1; + else if(o1->type > o2->type) + return 1; + + switch(o1->type) { + case _DT_STRING: + return _ht_cmp_string(o1->data.string, o2->data.string); +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + if(o1->data.instance->compare) { + cmp = o1->data.instance->compare; + fst = true; + } else if(o2->data.instance->compare) { + cmp = o2->data.instance->compare; + fst = false; + } + if(cmp) { + mb_interpreter_t* s = o1->data.instance->ref.s; + _ls_node_t ast; + _ls_node_t* tmp = * + mb_value_t va[1]; + mb_make_nil(va[0]); + _internal_object_to_public_value(fst ? o2 : o1, &va[0]); + memset(&ast, 0, sizeof(_ls_node_t)); + if(_eval_routine(s, &tmp, va, 1, cmp, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { + _MAKE_NIL(&val); + _public_value_to_internal_object(&s->running_context->intermediate_value, &val); + if(val.type != _DT_INT) { + int ignored = MB_FUNC_OK; + _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, o1, MB_FUNC_ERR, _exit, ignored); + } + + return (int)(fst ? val.data.integer : -val.data.integer); + } + } + + goto _default; +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + if(o1->data.usertype_ref->cmp) + return o1->data.usertype_ref->cmp(o1->data.usertype_ref->ref.s, o1->data.usertype_ref->usertype, o2->data.usertype_ref->usertype); + else if(o2->data.usertype_ref->cmp) + return o2->data.usertype_ref->cmp(o1->data.usertype_ref->ref.s, o1->data.usertype_ref->usertype, o2->data.usertype_ref->usertype); + + goto _default; +#endif /* MB_ENABLE_USERTYPE_REF */ + default: +#if defined MB_ENABLE_CLASS || defined MB_ENABLE_USERTYPE_REF +_default: +#endif /* MB_ENABLE_CLASS || MB_ENABLE_USERTYPE_REF */ + if(mb_is_little_endian()) { + for(i = (int)sizeof(_raw_t) - 1; i >= 0; --i) { + if(o1->data.raw[i] < o2->data.raw[i]) + return -1; + else if(o1->data.raw[i] > o2->data.raw[i]) + return 1; + } + } else { + for(i = 0; i < (int)sizeof(_raw_t); ++i) { + if(o1->data.raw[i] < o2->data.raw[i]) + return -1; + else if(o1->data.raw[i] > o2->data.raw[i]) + return 1; + } + } + + break; + } + + goto _exit; /* Avoid an unreferenced label warning */ + +_exit: + return 0; +} + +static int _ht_cmp_string(void* d1, void* d2) { + char* s1 = (char*)d1; + char* s2 = (char*)d2; + + return strcmp(s1, s2); +} + +static int _ht_cmp_intptr(void* d1, void* d2) { + intptr_t i1 = *(intptr_t*)d1; + intptr_t i2 = *(intptr_t*)d2; + + if(i1 < i2) + return -1; + else if(i1 > i2) + return 1; + + return 0; +} + +static int _ht_cmp_ref(void* d1, void* d2) { + _ref_t* r1 = (_ref_t*)d1; + _ref_t* r2 = (_ref_t*)d2; + intptr_t i = (intptr_t)r1 - (intptr_t)r2; + + if(i < 0) + return -1; + else if(i > 0) + return 1; + + return 0; +} + +static _ht_node_t* _ht_create(unsigned size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra) { + const unsigned array_size = size ? size : _HT_ARRAY_SIZE_DEFAULT; + _ht_node_t* result = 0; + unsigned ul = 0; + + if(!cmp) + cmp = _ht_cmp_intptr; + if(!hs) + hs = _ht_hash_intptr; + + result = (_ht_node_t*)mb_malloc(sizeof(_ht_node_t)); + result->free_extra = freeextra; + result->compare = cmp; + result->hash = hs; + result->array_size = array_size; + result->count = 0; +#if _LAZY_HASH_TABLE + mb_unrefvar(ul); + + result->array = 0; +#else /* _LAZY_HASH_TABLE */ + result->array = (_ls_node_t**)mb_malloc(sizeof(_ls_node_t*) * result->array_size); + for(ul = 0; ul < result->array_size; ++ul) + result->array[ul] = _ls_create(); +#endif /* _LAZY_HASH_TABLE */ + + return result; +} + +static _ls_node_t* _ht_find(_ht_node_t* ht, void* key) { + _ls_node_t* result = 0; + _ls_node_t* bucket = 0; + unsigned hash_code = 0; + + mb_assert(ht && key); + + hash_code = ht->hash(ht, key); + if(ht->array && ht->array[hash_code]) { + bucket = ht->array[hash_code]; + bucket = bucket->next; + } + while(bucket) { + if(ht->compare(bucket->extra, key) == 0) { + result = bucket; + + break; + } + bucket = bucket->next; + } + + return result; +} + +static unsigned _ht_set_or_insert(_ht_node_t* ht, void* key, void* value) { + unsigned result = 0; + _ls_node_t* bucket = 0; + unsigned hash_code = 0; + unsigned ul = 0; + + mb_assert(ht && key); + + bucket = _ht_find(ht, key); + if(bucket) { /* Update */ + bucket->data = value; + ++result; + } else { /* Insert */ + hash_code = ht->hash(ht, key); + if(!ht->array) { + ht->array = (_ls_node_t**)mb_malloc(sizeof(_ls_node_t*) * ht->array_size); + for(ul = 0; ul < ht->array_size; ++ul) + ht->array[ul] = 0; + } + if(!ht->array[hash_code]) + ht->array[hash_code] = _ls_create(); + bucket = ht->array[hash_code]; + bucket = _ls_pushback(bucket, value); + mb_assert(bucket); + bucket->extra = key; + ++ht->count; + ++result; + } + + return result; +} + +static unsigned _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp) { + unsigned result = 0; + unsigned hash_code = 0; + _ls_node_t* bucket = 0; + + mb_assert(ht && key); + + if(!cmp) + cmp = _ls_cmp_extra; + + bucket = _ht_find(ht, key); + hash_code = ht->hash(ht, key); + if(ht->array && ht->array[hash_code]) { + bucket = ht->array[hash_code]; + result = _ls_try_remove(bucket, key, cmp, ht->free_extra); + ht->count -= result; + } + + return result; +} + +static unsigned _ht_foreach(_ht_node_t* ht, _ht_operation op) { + unsigned result = 0; + _ls_node_t* bucket = 0; + unsigned ul = 0; + + if(ht->array) { + for(ul = 0; ul < ht->array_size; ++ul) { + bucket = ht->array[ul]; + if(bucket) + result += _ls_foreach(bucket, op); + } + } + + return result; +} + +static unsigned _ht_count(_ht_node_t* ht) { + mb_assert(ht); + + return ht->count; +} + +static void _ht_clear(_ht_node_t* ht) { + unsigned ul = 0; + + mb_assert(ht); + + if(ht->array) { + for(ul = 0; ul < ht->array_size; ++ul) { + if(ht->array[ul]) + _ls_clear(ht->array[ul]); + } + ht->count = 0; + } +} + +static void _ht_destroy(_ht_node_t* ht) { + unsigned ul = 0; + + mb_assert(ht); + + if(ht->array) { + if(ht->free_extra) + _ht_foreach(ht, ht->free_extra); + + for(ul = 0; ul < ht->array_size; ++ul) { + if(ht->array[ul]) + _ls_destroy(ht->array[ul]); + } + + safe_free(ht->array); + } + safe_free(ht); +} + +static int _ht_remove_exist(void* data, void* extra, _ht_node_t* ht) { + int result = _OP_RESULT_NORMAL; + mb_unrefvar(data); + + if(_ht_find(ht, extra)) + _ht_remove(ht, extra, 0); + + return result; +} + +/** Memory manipulations */ + +/* Initialize a chunk of resizable dynamic buffer */ +static void _init_dynamic_buffer(_dynamic_buffer_t* buf) { + mb_assert(buf); + + memset(buf->bytes, 0, sizeof(buf->bytes)); + buf->pointer.charp = buf->bytes; + buf->size = sizeof(buf->bytes); +} + +/* Dispose a chunk of resizable dynamic buffer */ +static void _dispose_dynamic_buffer(_dynamic_buffer_t* buf) { + mb_assert(buf); + + if(buf->pointer.charp != buf->bytes) { + safe_free(buf->pointer.charp); + } + buf->pointer.charp = 0; + buf->size = 0; +} + +/* Get the element count of a chunk of resizable dynamic buffer */ +static size_t _countof_dynamic_buffer(_dynamic_buffer_t* buf, size_t es) { + mb_assert(buf); + + return buf->size / es; +} + +/* Resize a chunk of resizable dynamic buffer */ +static void _resize_dynamic_buffer(_dynamic_buffer_t* buf, size_t es, size_t c) { + size_t as = es * c; + + mb_assert(buf); + + if(as > buf->size) { + if(buf->pointer.charp != buf->bytes) { + safe_free(buf->pointer.charp); + } + buf->pointer.charp = (char*)mb_malloc(as); + buf->size = as; + } +} + +/* Allocate a chunk of memory with a specific size */ +static void* mb_malloc(size_t s) { + char* ret = 0; + size_t rs = s; + +#ifdef MB_ENABLE_ALLOC_STAT + if(!_MB_CHECK_MEM_TAG_SIZE(size_t, s)) + return 0; + rs += _MB_MEM_TAG_SIZE; +#endif /* MB_ENABLE_ALLOC_STAT */ + if(_mb_allocate_func) + ret = _mb_allocate_func((unsigned)rs); + else + ret = (char*)malloc(rs); + mb_assert(ret); +#ifdef MB_ENABLE_ALLOC_STAT + _mb_allocated += s; + ret += _MB_MEM_TAG_SIZE; + _MB_WRITE_MEM_TAG_SIZE(ret, s); +#endif /* MB_ENABLE_ALLOC_STAT */ + + return (void*)ret; +} + +/* Free a chunk of memory */ +static void mb_free(void* p) { + mb_assert(p); + +#ifdef MB_ENABLE_ALLOC_STAT + do { + size_t os = _MB_READ_MEM_TAG_SIZE(p); + _mb_allocated -= os; + p = (char*)p - _MB_MEM_TAG_SIZE; + } while(0); +#endif /* MB_ENABLE_ALLOC_STAT */ + if(_mb_free_func) + _mb_free_func((char*)p); + else + free(p); +} + +/* Compare two chunks of memory */ +static int mb_memcmp(void* l, void* r, size_t s) { + unsigned char* lc = (unsigned char*)l; + unsigned char* rc = (unsigned char*)r; + int i = 0; + + if(mb_is_little_endian()) { + for(i = (int)s - 1; i >= 0; i--) { + if(lc[i] < rc[i]) + return -1; + else if(lc[i] > rc[i]) + return 1; + } + } else { + for(i = 0; i < (int)s; i++) { + if(lc[i] < rc[i]) + return -1; + else if(lc[i] > rc[i]) + return 1; + } + } + + return 0; +} + +/* Detect whether a chunk of memory contains any non-zero byte */ +static 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; +} + +/* Duplicate a string */ +static char* mb_strdup(const char* p, size_t s) { +#ifdef MB_ENABLE_ALLOC_STAT + if(!s) { + s = _MB_READ_MEM_TAG_SIZE(p); + } + + return mb_memdup(p, (unsigned)s); +#else /* MB_ENABLE_ALLOC_STAT */ + if(s) + return mb_memdup(p, (unsigned)s); + + return mb_memdup(p, (unsigned)(strlen(p) + 1)); +#endif /* MB_ENABLE_ALLOC_STAT */ +} + +/* Change a string to upper case */ +static char* mb_strupr(char* s) { + char* t = s; + + while(*s) { + *s = toupper(*s); + ++s; + } + + return t; +} + +/* Determine whether it's running on a little endian platform */ +static bool_t mb_is_little_endian(void) { + int i = 1; + + return ((char*)&i)[0] == 1; +} + +/** Unicode handling */ + +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE +/* Map a UTF8 character string to a UTF16 (wide character) string */ +static int mb_bytes_to_wchar(const char* sz, wchar_t** out, size_t size) { + int result = MultiByteToWideChar(CP_UTF8, 0, sz, -1, 0, 0); + if((int)size >= result) + MultiByteToWideChar(CP_UTF8, 0, sz, -1, *out, result); + + return result; +} + +/* Map an ANSI character string to a UTF16 (wide character) string */ +static int mb_bytes_to_wchar_ansi(const char* sz, wchar_t** out, size_t size) { + int result = MultiByteToWideChar(CP_ACP, 0, sz, -1, 0, 0); + if((int)size >= result) + MultiByteToWideChar(CP_ACP, 0, sz, -1, *out, result); + + return result; +} + +/* Map a UTF16 (wide character) string to a UTF8 character string */ +static int mb_wchar_to_bytes(const wchar_t* sz, char** out, size_t size) { + int result = WideCharToMultiByte(CP_UTF8, 0, sz, -1, 0, 0, 0, 0); + if((int)size >= result) + WideCharToMultiByte(CP_UTF8, 0, sz, -1, *out, result, 0, 0); + + return result; +} +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ + +/* Determine whether a string begins with a BOM, and ignore it */ +static int mb_uu_getbom(const char** ch) { + if(!ch && !(*ch)) + return 0; + + if((*ch)[0] == -17 && (*ch)[1] == -69 && (*ch)[2] == -65) { + *ch += 3; + + return 3; + } else if((*ch)[0] == -2 && (*ch)[1] == -1) { + *ch += 2; + + return 2; + } + + return 0; +} + +#ifdef MB_ENABLE_UNICODE +/* Determine whether a buffer is a UTF8 encoded character, and return taken bytes */ +static int mb_uu_ischar(const char* ch) { + /* Copyright 2008, 2009 Bjoern Hoehrmann, http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ */ +# define _TAKE(__ch, __c, __r) do { __c = *__ch++; __r++; } while(0) +# define _COPY(__ch, __c, __r, __cp) do { _TAKE(__ch, __c, __r); __cp = (__cp << 6) | ((unsigned char)__c & 0x3Fu); } while(0) +# define _TRANS(__m, __cp, __g) do { __cp &= ((__g[(unsigned char)c] & __m) != 0); } while(0) +# define _TAIL(__ch, __c, __r, __cp, __g) do { _COPY(__ch, __c, __r, __cp); _TRANS(0x70, __cp, __g); } while(0) + + static const unsigned char range[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 + }; + + int result = 0; + unsigned codepoint = 0; + unsigned char type = 0; + char c = 0; + + if(!ch) + return 0; + + _TAKE(ch, c, result); + if(!(c & 0x80)) { + codepoint = (unsigned char)c; + + return 1; + } + + type = range[(unsigned char)c]; + codepoint = (0xFF >> type) & (unsigned char)c; + + switch(type) { + case 2: _TAIL(ch, c, result, codepoint, range); return result; + case 3: _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + case 4: _COPY(ch, c, result, codepoint); _TRANS(0x50, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + case 5: _COPY(ch, c, result, codepoint); _TRANS(0x10, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + case 6: _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + case 10: _COPY(ch, c, result, codepoint); _TRANS(0x20, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + case 11: _COPY(ch, c, result, codepoint); _TRANS(0x60, codepoint, range); _TAIL(ch, c, result, codepoint, range); _TAIL(ch, c, result, codepoint, range); return result; + default: return 0; + } + +# undef _TAKE +# undef _COPY +# undef _TRANS +# undef _TAIL +} + +/* Tell how many UTF8 character are there in a string */ +static int mb_uu_strlen(const char* ch) { + int result = 0; + + if(!ch) + return 0; + + while(*ch) { + int t = mb_uu_ischar(ch); + if(t <= 0) + return t; + ch += t; + result++; + } + + return result; +} + +/* Retrieve a sub string of a UTF8 string */ +static int mb_uu_substr(const char* ch, int begin, int count, char** o) { + int cnt = 0; + const char* b = 0; + const char* e = 0; + int l = 0; + + if(!ch || begin < 0 || count <= 0 || !o) + return -1; + + while(*ch) { + int t = mb_uu_ischar(ch); + if(t <= 0) + return t; + if(cnt == begin) { + b = ch; + + break; + } + ch += t; + cnt++; + } + + while(*ch) { + int t = mb_uu_ischar(ch); + if(t <= 0) + return t; + if(cnt == begin + count) { + e = ch; + + break; + } + ch += t; + e = ch; + cnt++; + } + + if(!(*ch) && (cnt != begin + count)) + return -1; + + l = (int)(e - b); + *o = (char*)mb_malloc(l + 1); + memcpy(*o, b, l); + (*o)[l] = _ZERO_CHAR; + + return l; +} +#endif /* MB_ENABLE_UNICODE */ + +/** Expression processing */ + +/* Determine whether a function is an operator */ +static bool_t _is_operator(mb_func_t op) { + return ( + (op == _core_dummy_assign) || + (op == _core_add) || + (op == _core_min) || + (op == _core_mul) || + (op == _core_div) || + (op == _core_mod) || + (op == _core_pow) || + (op == _core_open_bracket) || + (op == _core_close_bracket) || + (op == _core_equal) || + (op == _core_less) || + (op == _core_greater) || + (op == _core_less_equal) || + (op == _core_greater_equal) || + (op == _core_not_equal) || + (op == _core_and) || + (op == _core_or) || + (op == _core_is) + ); +} + +/* Determine whether a function is for flow control */ +static bool_t _is_flow(mb_func_t op) { + return ( + (op == _core_if) || + (op == _core_then) || + (op == _core_elseif) || + (op == _core_else) || + (op == _core_endif) || + (op == _core_for) || + (op == _core_to) || + (op == _core_step) || + (op == _core_next) || + (op == _core_while) || + (op == _core_wend) || + (op == _core_do) || + (op == _core_until) || + (op == _core_exit) || + (op == _core_goto) || + (op == _core_gosub) || + (op == _core_return) || + (op == _core_end) + ); +} + +/* Determine whether a function is unary */ +static bool_t _is_unary(mb_func_t op) { + return (op == _core_neg) || (op == _core_not); +} + +/* Determine whether a function is binary */ +static bool_t _is_binary(mb_func_t op) { + return ( + (op == _core_add) || + (op == _core_min) || + (op == _core_mul) || + (op == _core_div) || + (op == _core_mod) || + (op == _core_pow) || + (op == _core_equal) || + (op == _core_less) || + (op == _core_greater) || + (op == _core_less_equal) || + (op == _core_greater_equal) || + (op == _core_not_equal) || + (op == _core_and) || + (op == _core_or) || + (op == _core_is) + ); +} + +/* Get the priority of two operators */ +static char _get_priority(mb_func_t op1, mb_func_t op2) { + char result = _ZERO_CHAR; + int idx1 = 0; + int idx2 = 0; + + mb_assert(op1 && op2); + + idx1 = _get_priority_index(op1); + idx2 = _get_priority_index(op2); + mb_assert(idx1 < countof(_PRECEDE_TABLE) && idx2 < countof(_PRECEDE_TABLE[0])); + result = _PRECEDE_TABLE[idx1][idx2]; + + return result; +} + +/* Get the index of an operator in the priority table */ +static int _get_priority_index(mb_func_t op) { + int i = 0; + + mb_func_t funcs[] = { + _core_add, + _core_min, + _core_mul, + _core_div, + _core_mod, + _core_pow, + _core_open_bracket, + _core_close_bracket, + _core_dummy_assign, + _core_greater, + _core_less, + _core_greater_equal, + _core_less_equal, + _core_equal, + _core_not_equal, + _core_and, + _core_or, + _core_not, + _core_neg, + _core_is + }; + + mb_assert(op); + + for(i = 0; i < countof(funcs); i++) { + if(op == funcs[i]) + return i; + } + + mb_assert(0 && "Unknown operator."); + + return -1; +} + +/* Operate two operands */ +static _object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status) { + _object_t* result = 0; + _tuple3_t tp; + _tuple3_t* tpptr = 0; + int _status = 0; + + mb_assert(s && optr); + mb_assert(optr->type == _DT_FUNC); + + if(!opnd1) + return result; + + result = _create_object(); + + memset(&tp, 0, sizeof(_tuple3_t)); + tp.e1 = opnd1; + tp.e2 = opnd2; + tp.e3 = result; + tpptr = &tp; + + _status = (optr->data.func->pointer)(s, (void**)&tpptr); + if(status) + *status = _status; + if(_status != MB_FUNC_OK) { + if(_status != MB_FUNC_WARNING) { + safe_free(result); + } + _set_current_error(s, SE_RN_OPERATION_FAILED, 0); +#ifdef MB_ENABLE_SOURCE_TRACE + _set_error_pos(s, optr->source_pos, optr->source_row, optr->source_col); +#else /* MB_ENABLE_SOURCE_TRACE */ + _set_error_pos(s, 0, 0, 0); +#endif /* MB_ENABLE_SOURCE_TRACE */ + } + + return result; +} + +/* Determine whether an object is an expression termination */ +static bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj) { + bool_t result = false; + + mb_assert(s && obj); + + result = ( + (obj->type == _DT_EOS) || + (obj->type == _DT_SEP) || + (obj->type == _DT_FUNC && + (obj->data.func->pointer == _core_then || + obj->data.func->pointer == _core_elseif || + obj->data.func->pointer == _core_else || + obj->data.func->pointer == _core_endif || + obj->data.func->pointer == _core_to || + obj->data.func->pointer == _core_step)) + ); + + return result; +} + +/* Determine whether an object is an unexpected calculation result */ +static bool_t _is_unexpected_calc_type(mb_interpreter_t* s, _object_t* obj) { + mb_assert(s); + + return !obj || ( + (obj->type == _DT_FUNC) || + (obj->type == _DT_LABEL) || +#ifdef MB_ENABLE_LAMBDA + (obj->type == _DT_OUTER_SCOPE) || +#endif /* MB_ENABLE_LAMBDA */ + (obj->type == _DT_SEP) || +#ifdef MB_ENABLE_SOURCE_TRACE + (obj->type == _DT_PREV_IMPORT) || + (obj->type == _DT_POST_IMPORT) || +#endif /* MB_ENABLE_SOURCE_TRACE */ + (obj->type == _DT_EOS) + ); +} + +/* Determine whether an object is a referenced calculation result */ +static bool_t _is_referenced_calc_type(mb_interpreter_t* s, _object_t* obj) { + mb_assert(s && obj); + + return ( +#ifdef MB_ENABLE_USERTYPE_REF + (obj->type == _DT_USERTYPE_REF) || +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_COLLECTION_LIB + (obj->type == _DT_LIST) || (obj->type == _DT_DICT) || (obj->type == _DT_LIST_IT) || (obj->type == _DT_DICT_IT) || +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + (obj->type == _DT_CLASS) || +#endif /* MB_ENABLE_CLASS */ + (obj->type == _DT_ARRAY) || + (obj->type == _DT_ROUTINE) + ); +} + +/* Calculate an expression */ +static int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val) { + int result = 0; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + register _ls_node_t* garbage = 0; + register _ls_node_t* optr = 0; + register _ls_node_t* opnd = 0; + register _object_t* c = 0; + register _object_t* x = 0; + register _object_t* a = 0; + register _object_t* b = 0; + register _object_t* r = 0; + register _object_t* theta = 0; + char pri = _ZERO_CHAR; + int* inep = 0; + int f = 0; + + _object_t* guard_val = 0; + int bracket_count = 0; + bool_t hack = false; + _ls_node_t* errn = 0; + + mb_assert(s && l); + + running = s->running_context; + ast = *l; + garbage = _ls_create(); + optr = _ls_create(); + opnd = _ls_create(); + + inep = (int*)mb_malloc(sizeof(int)); + *inep = 0; + _ls_pushback(s->in_neg_expr, inep); + + c = (_object_t*)ast->data; + do { + if(c->type == _DT_STRING) { + if(ast->next) { + _object_t* _fsn = (_object_t*)ast->next->data; + if(_IS_FUNC(_fsn, _core_add) || _IS_FUNC(_fsn, _core_and) || _IS_FUNC(_fsn, _core_or) || _IS_FUNC(_fsn, _core_is)) + break; + } + + (*val)->type = _DT_STRING; + (*val)->data.string = c->data.string; + (*val)->ref = true; + ast = ast->next; + + goto _exit; + } + } while(0); + guard_val = c; + ast = ast->next; + _ls_pushback(optr, _exp_assign); + while( + !(c->type == _DT_FUNC && strcmp(c->data.func->name, _DUMMY_ASSIGN_CHAR) == 0) || + !(((_object_t*)(_ls_back(optr)->data))->type == _DT_FUNC && strcmp(((_object_t*)(_ls_back(optr)->data))->data.func->name, _DUMMY_ASSIGN_CHAR) == 0)) { + if(!hack) { + if(_IS_FUNC(c, _core_open_bracket)) { + ++bracket_count; + } else if(_IS_FUNC(c, _core_close_bracket)) { + --bracket_count; + if(bracket_count < 0) { + c = _exp_assign; + ast = ast->prev; + + continue; + } + } + } + hack = false; + if(!(c->type == _DT_FUNC && _is_operator(c->data.func->pointer))) { + if(_is_expression_terminal(s, c)) { + c = _exp_assign; + if(ast) + ast = ast->prev; + if(bracket_count) { + _object_t _cb; + _func_t _cbf; + _MAKE_NIL(&_cb); + _cb.type = _DT_FUNC; + _cb.data.func = &_cbf; + _cb.data.func->name = ")"; + _cb.data.func->pointer = _core_close_bracket; + while(bracket_count) { + _ls_pushback(optr, &_cb); + bracket_count--; + f = 0; + } + errn = ast; + } + } else { + if(c->type == _DT_ARRAY) { + unsigned arr_idx = 0; + mb_value_u arr_val; + _data_e arr_type; + _object_t* arr_elem = 0; + +#ifdef MB_ENABLE_CLASS + if(s->last_instance) { + _ls_node_t* cs = _search_identifier_in_scope_chain(s, 0, c->data.array->name, _PATHING_NORMAL, 0, 0); + if(cs) + c = (_object_t*)cs->data; + } +#endif /* MB_ENABLE_CLASS */ +_array: + if(ast && !_IS_FUNC(((_object_t*)ast->data), _core_open_bracket)) { + _ls_pushback(opnd, c); + f++; + } else { + ast = ast->prev; + result = _get_array_index(s, &ast, c, &arr_idx, 0); + if(result != MB_FUNC_OK) { + _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + ast = ast->next; + _get_array_elem(s, c->data.array, arr_idx, &arr_val, &arr_type); + arr_elem = _create_object(); + _ls_pushback(garbage, arr_elem); + arr_elem->type = arr_type; + arr_elem->ref = true; + _COPY_BYTES(arr_elem->data.bytes, arr_val.bytes); + if(f) { + _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + _ls_pushback(opnd, arr_elem); + f++; + } + } else if(c->type == _DT_FUNC) { + ast = ast->prev; + if(_IS_UNARY_FUNC(c)) { +#ifdef MB_ENABLE_STACK_TRACE + _ls_pushback(s->stack_frames, c->data.func->name); +#endif /* MB_ENABLE_STACK_TRACE */ + result = (c->data.func->pointer)(s, (void**)&ast); +#ifdef MB_ENABLE_STACK_TRACE + _ls_popback(s->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + } else { + int calc_depth = running->calc_depth; + running->calc_depth = _INFINITY_CALC_DEPTH; +#ifdef MB_ENABLE_STACK_TRACE + _ls_pushback(s->stack_frames, c->data.func->name); +#endif /* MB_ENABLE_STACK_TRACE */ + result = (c->data.func->pointer)(s, (void**)&ast); +#ifdef MB_ENABLE_STACK_TRACE + _ls_popback(s->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + running->calc_depth = calc_depth; + } + if(result != MB_FUNC_OK) { + _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + c = _create_object(); + _ls_pushback(garbage, c); + result = _public_value_to_internal_object(&running->intermediate_value, c); + if(c->type == _DT_STRING) + c->ref = true; + if(result != MB_FUNC_OK) + goto _error; + if(f) { + _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + if(_is_array(c)) { + goto _array; + } else { + if(ast && _IS_FUNC(ast->data, _core_open_bracket)) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + } + _ls_pushback(opnd, c); + f++; + } else if(c->type == _DT_ROUTINE) { +#ifdef MB_ENABLE_CLASS + bool_t calling = false; + _object_t* obj = 0; + _ls_node_t* fn = 0; +#endif /* MB_ENABLE_CLASS */ +_routine: + ast = ast->prev; +#ifdef MB_ENABLE_CLASS + calling = s->calling; + s->calling = false; +#endif /* MB_ENABLE_CLASS */ + result = _eval_routine(s, &ast, 0, 0, c->data.routine, _has_routine_lex_arg, _pop_routine_lex_arg); +#ifdef MB_ENABLE_CLASS + s->calling = calling; +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_CLASS + obj = (_object_t*)ast->data; + if(_IS_VAR(obj) && _is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { + if(fn) { + if(ast) ast = ast->next; + obj = (_object_t*)fn->data; + if(_IS_VAR(obj)) { + c = obj; + + goto _var; + } + } + } +#endif /* MB_ENABLE_CLASS */ + if(ast) + ast = ast->prev; + if(result != MB_FUNC_OK) { + _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + c = _create_object(); + _ls_pushback(garbage, c); + result = _public_value_to_internal_object(&running->intermediate_value, c); + if(result != MB_FUNC_OK) + goto _error; + if(f) { + _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + _ls_pushback(opnd, c); + f++; + } else if(c->type == _DT_VAR && c->data.variable->data->type == _DT_ARRAY) { + unsigned arr_idx = 0; + mb_value_u arr_val; + _data_e arr_type; + _object_t* arr_elem = 0; + + if(ast && !_IS_FUNC(((_object_t*)ast->data), _core_open_bracket)) { + c = c->data.variable->data; + _ls_pushback(opnd, c); + f++; + } else { + ast = ast->prev; + result = _get_array_index(s, &ast, 0, &arr_idx, 0); + if(result != MB_FUNC_OK) { + _handle_error_on_obj(s, SE_RN_CALCULATION_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + ast = ast->next; + _get_array_elem(s, c->data.variable->data->data.array, arr_idx, &arr_val, &arr_type); + arr_elem = _create_object(); + _ls_pushback(garbage, arr_elem); + arr_elem->type = arr_type; + arr_elem->ref = true; + if(arr_type == _DT_INT) { + arr_elem->data.integer = arr_val.integer; + } else if(arr_type == _DT_REAL) { + arr_elem->data.float_point = arr_val.float_point; + } else if(arr_type == _DT_STRING) { + arr_elem->data.string = arr_val.string; + } else if(arr_type == _DT_USERTYPE) { + arr_elem->data.usertype = arr_val.usertype; + } else { + mb_assert(0 && "Unsupported."); + } + if(f) { + _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + _ls_pushback(opnd, arr_elem); + f++; + } + } else { + if(c->type == _DT_VAR) { + _ls_node_t* cs = _search_identifier_in_scope_chain(s, 0, c->data.variable->name, +#ifdef MB_ENABLE_CLASS + _PU(c->data.variable->pathing), +#else /* MB_ENABLE_CLASS */ + 0, +#endif /* MB_ENABLE_CLASS */ + 0, + 0 + ); + if(cs) { + c = (_object_t*)cs->data; + if(c && c->type == _DT_VAR && c->data.variable->data->type == _DT_ROUTINE) + c = c->data.variable->data; + if(ast && ast && _IS_FUNC(ast->data, _core_open_bracket)) { + if(c && c->type == _DT_ROUTINE) + goto _routine; + } + } +#ifdef MB_ENABLE_CLASS +_var: +#endif /* MB_ENABLE_CLASS */ + if(ast) { + _object_t* _err_or_bracket = (_object_t*)ast->data; + do { +#ifdef MB_ENABLE_COLLECTION_LIB + if(_IS_VAR(c) && _IS_COLL(c->data.variable->data)) { + if(_IS_FUNC(_err_or_bracket, _core_open_bracket)) { + int_t idx = 0; + mb_value_t key; + mb_value_t ret; + _object_t* ocoll = c->data.variable->data; + + mb_make_nil(ret); + + *l = ast->prev; + + _mb_check(mb_attempt_open_bracket(s, (void**)l), _error); + + switch(ocoll->type) { + case _DT_LIST: + _mb_check(mb_pop_int(s, (void**)l, &idx), _error); + if(!_at_list(ocoll->data.list, idx, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _error, result); + } + + break; + case _DT_DICT: + mb_make_nil(key); + _mb_check(mb_pop_value(s, (void**)l, &key), _error); + if(!_find_dict(ocoll->data.dict, &key, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _error, result); + } + + break; + default: /* Do nothing */ + break; + } + + _mb_check(mb_attempt_close_bracket(s, (void**)l), _error); + + c = _create_object(); + _ls_pushback(garbage, c); + _public_value_to_internal_object(&ret, c); + + ast = *l; + } + + break; + } +#endif /* MB_ENABLE_COLLECTION_LIB */ + if(_IS_FUNC(_err_or_bracket, _core_open_bracket)) { + _handle_error_on_obj(s, SE_RN_INVALID_ID_USAGE, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + } while(0); + } + } + if(f) { + _handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + _ls_pushback(opnd, c); + f++; + } + if(running->calc_depth != _INFINITY_CALC_DEPTH) + running->calc_depth--; + if(ast && (running->calc_depth == _INFINITY_CALC_DEPTH || running->calc_depth)) { + c = (_object_t*)ast->data; + if(c->type == _DT_FUNC && !_is_operator(c->data.func->pointer) && !_is_flow(c->data.func->pointer)) { + _ls_foreach(opnd, _remove_source_object); + + _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + ast = ast->next; + } else { + c = _exp_assign; + } + } + } else { + pri = _get_priority(((_object_t*)(_ls_back(optr)->data))->data.func->pointer, c->data.func->pointer); + switch(pri) { + case '<': + _ls_pushback(optr, c); + c = (_object_t*)ast->data; + ast = ast->next; + f = 0; + + break; + case '=': + x = (_object_t*)_ls_popback(optr); + c = (_object_t*)ast->data; + ast = ast->next; + + break; + case '>': + theta = (_object_t*)_ls_popback(optr); + b = (_object_t*)_ls_popback(opnd); + a = (_object_t*)_ls_popback(opnd); + r = _operate_operand(s, theta, a, b, &result); + if(!r) { + _ls_clear(optr); + _handle_error_on_obj(s, SE_RN_OPERATION_FAILED, s->source_file, errn ? DON(errn) : DON(ast), MB_FUNC_ERR, _error, result); + } + _ls_pushback(opnd, r); + _ls_pushback(garbage, r); + if(_IS_FUNC(c, _core_close_bracket)) + hack = true; + + break; + case ' ': + _handle_error_on_obj(s, SE_RN_INVALID_OPERATION_USAGE, s->source_file, errn ? DON(errn) : DON(ast), MB_FUNC_ERR, _error, result); + + break; + } + } + } + + if(errn) { + _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(errn), MB_FUNC_ERR, _error, result); + } + + c = (_object_t*)(_ls_popback(opnd)); + if(_is_unexpected_calc_type(s, c)) { + _handle_error_on_obj(s, SE_RN_INVALID_DATA_TYPE, s->source_file, DON(ast), MB_FUNC_ERR, _error, result); + } + if(c->type == _DT_VAR) { + (*val)->type = c->data.variable->data->type; + (*val)->data = c->data.variable->data->data; + if(_is_string(c)) + (*val)->ref = true; + } else { + (*val)->type = c->type; + if(_is_string(c)) { + char* _str = _extract_string(c); + (*val)->data.string = mb_strdup(_str, strlen(_str) + 1); + (*val)->ref = false; + } else if(c->type == _DT_ARRAY) { + (*val)->data = c->data; + c->type = _DT_NIL; + } else { + (*val)->data = c->data; + } + } + if(guard_val != c && _ls_try_remove(garbage, c, _ls_cmp_data, 0)) { + _try_clear_intermediate_value(c, 0, s); + + if(_is_referenced_calc_type(s, c)) + _destroy_object_capsule_only(c, 0); + else + _destroy_object(c, 0); + } + + while(0) { +_error: + _LS_FOREACH(garbage, _do_nothing_on_object, _remove_if_exist, opnd); + } + +_exit: + _LS_FOREACH(garbage, _destroy_object, _try_clear_intermediate_value, s); + _ls_destroy(garbage); + _ls_foreach(optr, _destroy_object_not_compile_time); + _ls_foreach(opnd, _destroy_object_not_compile_time); + _ls_destroy(optr); + _ls_destroy(opnd); + *l = ast; + mb_free(_ls_popback(s->in_neg_expr)); + + return result; +} + +/* Push current variable argument list */ +static _ls_node_t* _push_var_args(mb_interpreter_t* s) { + _ls_node_t* result = s->var_args; + + s->var_args = 0; + + return result; +} + +/* Pop current variable argument list */ +static void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args) { + _ls_node_t* var_args = s->var_args; + + s->var_args = last_var_args; + if(var_args) { + _LS_FOREACH(var_args, _do_nothing_on_object, _destroy_var_arg, &s->gc); + _ls_destroy(var_args); + } +} + +/* Pop an argument from the caller or a variable argument list */ +static int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg) { + int result = MB_FUNC_OK; + _ls_node_t* ast = *l; + + mb_make_nil(*arg); + if(ast && ast->data && _IS_FUNC(ast->data, _core_args)) { + if(args) { + _object_t* obj = (_object_t*)_ls_popfront(args); + if(obj) { + _internal_object_to_public_value(obj, arg); + _destroy_object_capsule_only(obj, 0); + } + } else { + arg->type = MB_DT_UNKNOWN; + } + } else { + result = pop_arg(s, (void**)l, va, ca, ia, r, arg); + } + + return result; +} + +/* Process arguments of a routine */ +static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* parameters = 0; + mb_value_t arg; + _ls_node_t* pars = 0; + _var_t* var = 0; + _ls_node_t* rnode = 0; + unsigned ia = 0; + _ls_node_t* var_args = 0; + + parameters = r->func.basic.parameters; +#ifdef MB_ENABLE_LAMBDA + if(r->type == _IT_LAMBDA) + parameters = r->func.lambda.parameters; +#endif /* MB_ENABLE_LAMBDA */ + + if(parameters) { + mb_make_nil(arg); + pars = parameters; + pars = pars->next; + while(pars && (!has_arg || (has_arg && has_arg(s, (void**)l, va, ca, &ia, r)))) { + var = (_var_t*)pars->data; + pars = pars->next; + if(_IS_VAR_ARGS(var)) + break; + + if(pop_arg) { + mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); + } + + if(running->meta == _SCOPE_META_REF) { + _object_t* obj = (_object_t*)(_ht_find(running->var_dict, var->name)->data); + var = obj->data.variable; + + if(proc_ref) + var->data->ref = false; + } else { + rnode = _search_identifier_in_scope_chain(s, running, var->name, _PATHING_NONE, 0, 0); + if(rnode) + var = ((_object_t*)rnode->data)->data.variable; + + if(proc_ref) + var->data->ref = true; + } + + if(!pop_arg && var->data->type == _DT_STRING && !var->data->ref) + _mark_lazy_destroy_string(s, var->data->data.string); + result = _public_value_to_internal_object(&arg, var->data); + + if(result != MB_FUNC_OK) + break; + + if(args && _ls_empty(args)) + break; + } + + if(_IS_VAR_ARGS(var)) { + if(has_arg && !var_args && _IS_VAR_ARGS(var)) + var_args = s->var_args = _ls_create(); + + while(has_arg && has_arg(s, (void**)l, va, ca, &ia, r)) { + if(pop_arg) { + mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); + } + + if(var_args) { + _object_t* obj = _create_object(); + result = _public_value_to_internal_object(&arg, obj); + _ls_pushback(var_args, obj); + } + + if(args && _ls_empty(args)) + break; + } + } + + ast = *l; + if(ast) { + _object_t* obj = (_object_t*)ast->data; + if(obj && _IS_FUNC(obj, _core_args)) { + if(ast) ast = ast->next; + *l = ast; + } + } + } + + return result; +} + +/* Evaluate a routine */ +static int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { + int result = MB_FUNC_OK; +#ifdef MB_ENABLE_SOURCE_TRACE + char* src = 0; +#endif /* MB_ENABLE_SOURCE_TRACE */ +#if defined MB_ENABLE_STACK_TRACE && defined MB_ENABLE_LAMBDA + char ln[_LAMBDA_NAME_MAX_LENGTH]; +#endif /* MB_ENABLE_STACK_TRACE && MB_ENABLE_LAMBDA */ + +#ifdef MB_ENABLE_STACK_TRACE + _ls_pushback(s->stack_frames, r->name); +#endif /* MB_ENABLE_STACK_TRACE */ + +#ifdef MB_ENABLE_SOURCE_TRACE + src = s->source_file; + s->source_file = r->source_file; +#endif /* MB_ENABLE_SOURCE_TRACE */ + + if(r->type == _IT_SCRIPT && r->func.basic.entry) { + result = _eval_script_routine(s, l, va, ca, r, has_arg, pop_arg); +#ifdef MB_ENABLE_LAMBDA + } else if(r->type == _IT_LAMBDA && r->func.lambda.entry) { +# ifdef MB_ENABLE_STACK_TRACE + _ls_node_t* top = _ls_back(s->stack_frames); + if(top) { + sprintf(ln, "LAMBDA_0x%p", &r->func.lambda.ref); + top->data = ln; + } +# endif /* MB_ENABLE_STACK_TRACE */ + result = _eval_lambda_routine(s, l, va, ca, r, has_arg, pop_arg); +#endif /* MB_ENABLE_LAMBDA */ + } else if(r->type == _IT_NATIVE && r->func.native.entry) { + result = _eval_native_routine(s, l, va, ca, r, has_arg, pop_arg); + } else { + _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +_exit: +#ifdef MB_ENABLE_SOURCE_TRACE + s->source_file = src; +#endif /* MB_ENABLE_SOURCE_TRACE */ + +#ifdef MB_ENABLE_STACK_TRACE + _ls_popback(s->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + + return result; +} + +/* Evaluate a script routine */ +static int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + _routine_t* lastr = 0; + mb_value_t inte; + _ls_node_t* lastv = 0; + bool_t succ = false; +#ifdef MB_ENABLE_CLASS + bool_t pushed_inst = false; + _class_t* last_inst = 0; +#endif /* MB_ENABLE_CLASS */ + + mb_assert(s && l && r); + + if(!va && s->last_routine && !s->last_routine->func.basic.parameters && (s->last_routine->name == r->name || !strcmp(s->last_routine->name, r->name))) { + ast = *l; + _skip_to(s, &ast, 0, _DT_EOS); + if(ast && ((_object_t*)ast->data)->type == _DT_EOS) + ast = ast->next; + if(_IS_FUNC((_object_t*)ast->data, _core_enddef)) { /* Tail recursion optimization */ + *l = r->func.basic.entry; + if(*l) + *l = (*l)->next; + + goto _tail; + } + } + + lastr = s->last_routine; + s->last_routine = r; + + lastv = _push_var_args(s); + + if(!va) { + mb_check(mb_attempt_open_bracket(s, (void**)l)); + } + + running = _push_weak_scope_by_routine(s, r->func.basic.scope, r); + result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); + if(result != MB_FUNC_OK) { + if(running->meta == _SCOPE_META_REF) + _destroy_scope(s, running); + else + _pop_weak_scope(s, running); + + goto _error; + } + running = _pop_weak_scope(s, running); + + if(!va) { + _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); + } + + ast = *l; + _ls_pushback(s->sub_stack, ast); + +#ifdef MB_ENABLE_CLASS + if(r->instance && s->last_instance != r->instance) { + pushed_inst = true; + last_inst = s->last_instance; + s->last_instance = r->instance; + if(r->instance) + _push_scope_by_class(s, r->instance->scope); + } +#endif /* MB_ENABLE_CLASS */ + + running = _push_scope_by_routine(s, running); + + *l = r->func.basic.entry; + if(!(*l)) { + _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + do { + result = _execute_statement(s, l, true); + ast = *l; + if(result == MB_SUB_RETURN) { + result = MB_FUNC_OK; + + break; + } + if(result == MB_FUNC_SUSPEND && s->error_handler) { + _handle_error_now(s, SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, s->last_error_file, result); + + goto _exit; + } + if(result != MB_FUNC_OK && s->error_handler) { + if(result >= MB_EXTENDED_ABORT) + s->last_error = SE_EA_EXTENDED_ABORT; + _handle_error_now(s, s->last_error, s->last_error_file, result); + + goto _exit; + } + } while(ast); + +#ifdef MB_ENABLE_CLASS + _out_of_scope(s, running, r->instance, r, true); +#else /* MB_ENABLE_CLASS */ + _out_of_scope(s, running, 0, r, true); +#endif /* MB_ENABLE_CLASS */ + + result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); + if(result != MB_FUNC_OK) + goto _exit; + + succ = true; + + mb_make_nil(inte); + _swap_public_value(&inte, &running->intermediate_value); + + _pop_scope(s, true); + +_exit: + if(!succ) + _pop_scope(s, true); + +#ifdef MB_ENABLE_CLASS + if(pushed_inst) { + if(r->instance) + _pop_scope(s, false); + s->last_instance = last_inst; + } +#endif /* MB_ENABLE_CLASS */ + + if(succ) + _assign_public_value(&s->running_context->intermediate_value, &inte); + +_error: + s->last_routine = lastr; + + _pop_var_args(s, lastv); + +_tail: + return result; +} + +#ifdef MB_ENABLE_LAMBDA +/* Evaluate a lambda routine */ +static int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + _routine_t* lastr = 0; + mb_value_t inte; + _ls_node_t* lastv = 0; + + mb_assert(s && l && r); + + lastr = s->last_routine; + s->last_routine = r; + + lastv = _push_var_args(s); + + if(!va) { + mb_check(mb_attempt_open_bracket(s, (void**)l)); + } + + running = _link_lambda_scope_chain(s, &r->func.lambda, true); + result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); + ast = *l; + if(result != MB_FUNC_OK) { + _unlink_lambda_scope_chain(s, &r->func.lambda, true); + + goto _error; + } + running = _unlink_lambda_scope_chain(s, &r->func.lambda, true); + + if(!va) { + _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); + } + + ast = *l; + _ls_pushback(s->sub_stack, ast); + + running = _link_lambda_scope_chain(s, &r->func.lambda, false); + + *l = r->func.lambda.entry; + if(!(*l)) { + _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + do { + result = _execute_statement(s, l, true); + ast = *l; + if(result == MB_SUB_RETURN) { + result = MB_FUNC_OK; + + break; + } + if(result == MB_FUNC_SUSPEND && s->error_handler) { + _handle_error_now(s, SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, s->last_error_file, result); + + goto _exit; + } + if(result != MB_FUNC_OK && s->error_handler) { + if(result >= MB_EXTENDED_ABORT) + s->last_error = SE_EA_EXTENDED_ABORT; + _handle_error_now(s, s->last_error, s->last_error_file, result); + + goto _exit; + } + } while(ast); + + _out_of_scope(s, running, 0, r, true); + + result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); + if(result != MB_FUNC_OK) + goto _exit; + + mb_make_nil(inte); + _swap_public_value(&inte, &running->intermediate_value); + + running = _unlink_lambda_scope_chain(s, &r->func.lambda, false); + + _assign_public_value(&s->running_context->intermediate_value, &inte); + +_exit: + if(result != MB_FUNC_OK) + _unlink_lambda_scope_chain(s, &r->func.lambda, false); + +_error: + s->last_routine = lastr; + + _pop_var_args(s, lastv); + + *l = ast; + + return result; +} +#endif /* MB_ENABLE_LAMBDA */ + +/* Evaluate a native routine */ +static int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg) { + int result = MB_FUNC_OK; + _routine_t* lastr = 0; + mb_routine_func_t entry = 0; + _ls_node_t* lastv = 0; + + mb_assert(s && l && r); + + lastr = s->last_routine; + s->last_routine = r; + + lastv = _push_var_args(s); + + entry = r->func.native.entry; + if(!entry) { + _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + result = entry(s, (void**)l, va, ca, r, has_arg, pop_arg); + +_exit: + s->last_routine = lastr; + + _pop_var_args(s, lastv); + + return result; +} + +/* Detect if there is any more lexical argument */ +static int _has_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) { + mb_unrefvar(va); + mb_unrefvar(ca); + mb_unrefvar(ia); + mb_unrefvar(r); + + return mb_has_arg(s, l); +} + +/* Pop a lexical argument */ +static int _pop_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val) { + mb_unrefvar(va); + mb_unrefvar(ca); + mb_unrefvar(ia); + mb_unrefvar(r); + + return mb_pop_value(s, l, val); +} + +/* Detect if there is any more argument in the argument list */ +static int _has_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) { + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(va); + mb_unrefvar(r); + + return *ia < ca; +} + +/* Pop an argument from the argument list */ +static int _pop_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r, mb_value_t* val) { + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(ca); + mb_unrefvar(r); + + memcpy(val, &(va[*ia]), sizeof(mb_value_t)); + (*ia)++; + + return MB_FUNC_OK; +} + +/* Determine whether an object is a PRINT termination */ +static bool_t _is_print_terminal(mb_interpreter_t* s, _object_t* obj) { + bool_t result = false; + + mb_assert(s && obj); + + result = ( + _IS_EOS(obj) || + _IS_SEP(obj, ':') || + _IS_FUNC(obj, _core_elseif) || + _IS_FUNC(obj, _core_else) || + _IS_FUNC(obj, _core_endif) + ); + + return result; +} + +/* Try to call overridden function */ +static mb_meta_status_u _try_overridden(mb_interpreter_t* s, void** l, mb_value_t* d, const char* f, mb_meta_func_u t) { + mb_assert(s && l && d && f); + +#ifdef MB_ENABLE_USERTYPE_REF + if(d->type == MB_DT_USERTYPE_REF) { + _object_t obj; + _MAKE_NIL(&obj); + _public_value_to_internal_object(d, &obj); + if(t == MB_MF_COLL && obj.data.usertype_ref->coll_func) + return obj.data.usertype_ref->coll_func(s, l, f); + else if(t == MB_MF_FUNC && obj.data.usertype_ref->generic_func) + return obj.data.usertype_ref->generic_func(s, l, f); + } +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_CLASS + if(d->type == MB_DT_CLASS) { + char buf[_TEMP_FORMAT_MAX_LENGTH]; + _ls_node_t* ofn = 0; + _object_t obj; + _MAKE_NIL(&obj); + _public_value_to_internal_object(d, &obj); + sprintf(buf, _CLASS_OVERRIDE_FMT, f); + ofn = _search_identifier_in_class(s, obj.data.instance, buf, 0, 0); + if(ofn) { + _object_t* ofo = (_object_t*)ofn->data; + _ls_node_t* ast = (_ls_node_t*)*l; + mb_value_t va[1]; + mb_make_nil(va[0]); + if(_eval_routine(s, &ast, va, 0, ofo->data.routine, _has_routine_lex_arg, _pop_routine_lex_arg) == MB_FUNC_OK) { + if(ast) + *l = ast->prev; + + return (mb_meta_status_u)(MB_MS_DONE | MB_MS_RETURNED); + } + } + } +#endif /* MB_ENABLE_CLASS */ +#if !defined MB_ENABLE_USERTYPE_REF || !defined MB_ENABLE_CLASS + mb_unrefvar(t); +#endif /* !MB_ENABLE_USERTYPE_REF && !MB_ENABLE_CLASS */ + + return MB_MS_NONE; +} + +/** Handlers */ + +/* Set current error information */ +static void _set_current_error(mb_interpreter_t* s, mb_error_e err, char* f) { + mb_assert(s && err >= 0); + + if(s->last_error == SE_NO_ERR) { + s->last_error = err; + s->last_error_file = f; + } +} + +/* Get a print functor of an interpreter */ +static mb_print_func_t _get_printer(mb_interpreter_t* s) { + mb_assert(s); + + if(s->printer) + return s->printer; + + return printf; +} + +/* Get an input functor of an interpreter */ +static mb_input_func_t _get_inputer(mb_interpreter_t* s) { + mb_assert(s); + + if(s->inputer) + return s->inputer; + + return mb_gets; +} + +/* Print a string */ +static void _print_string(mb_interpreter_t* s, _object_t* obj) { + mb_assert(s && obj); + +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE + char* loc = setlocale(LC_ALL, ""); + char* str = obj->data.string ? obj->data.string : MB_NULL_STRING; + _dynamic_buffer_t buf; + size_t lbuf = 0; + _INIT_BUF(buf); + while((lbuf = (size_t)mb_bytes_to_wchar(str, &_WCHAR_BUF_PTR(buf), _WCHARS_OF_BUF(buf))) > _WCHARS_OF_BUF(buf)) { + _RESIZE_WCHAR_BUF(buf, lbuf); + } + _get_printer(s)("%ls", _WCHAR_BUF_PTR(buf)); + _DISPOSE_BUF(buf); + setlocale(LC_ALL, loc); +#else /* MB_CP_VC && MB_ENABLE_UNICODE */ + _get_printer(s)("%s", obj->data.string ? obj->data.string : MB_NULL_STRING); +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ +} + +/** Parsing helpers */ + +/* Read all content of a file into a buffer */ +static char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix) { +#ifndef MB_CP_ARDUINO + FILE* fp = 0; + char* buf = 0; + long curpos = 0; + long l = 0; + long i = 0; + _parsing_context_t* context = 0; + + mb_assert(s); + + context = (_parsing_context_t*)s->parsing_context; + + if(_ls_find(context->imported, (void*)f, (_ls_compare)_ht_cmp_string, 0)) { + buf = (char*)f; + } else { + fp = fopen(f, "rb"); + if(fp) { + buf = mb_strdup(f, strlen(f) + 1); + _ls_pushback(context->imported, buf); + buf = 0; + + curpos = ftell(fp); + fseek(fp, 0L, SEEK_END); + l = ftell(fp); + fseek(fp, curpos, SEEK_SET); + if(prefix) { + i = (long)strlen(prefix); + l += i; + } + buf = (char*)mb_malloc((size_t)(l + 1)); + mb_assert(buf); + if(prefix) + memcpy(buf, prefix, i); + fread(buf + i, 1, l, fp); + do { + char* off = buf + i; + int b = mb_uu_getbom((const char**)&off); + if(b) { + memmove(buf + i, buf + i + b, l - b - i); + buf[l - b] = _ZERO_CHAR; + } + } while(0); + fclose(fp); + buf[l] = _ZERO_CHAR; + } + } + + return buf; +#else /* MB_CP_ARDUINO */ + return 0; +#endif /* MB_CP_ARDUINO */ +} + +/* Finish loading a file */ +static void _end_of_file(_parsing_context_t* context) { + if(context) + context->parsing_state = _PS_NORMAL; +} + +/* Determine whether a character is blank */ +static bool_t _is_blank_char(char c) { + return (c == ' ') || (c == '\t'); +} + +/* Determine whether a character is end of file */ +static bool_t _is_eof_char(char c) { + return (c == EOF); +} + +/* Determine whether a character is newline */ +static bool_t _is_newline_char(char c) { + return (c == _RETURN_CHAR) || (c == _NEWLINE_CHAR) || _is_eof_char(c); +} + +/* Determine whether a character is separator */ +static bool_t _is_separator_char(char c) { + return (c == ',') || (c == ';') || (c == ':'); +} + +/* Determine whether a character is bracket */ +static bool_t _is_bracket_char(char c) { + return (c == '(') || (c == ')'); +} + +/* Determine whether a character is quotation mark */ +static bool_t _is_quotation_char(char c) { + return (c == '"'); +} + +/* Determine whether a character is comment mark */ +static bool_t _is_comment_char(char c) { + return (c == '\''); +} + +/* Determine whether a character is accessor char */ +static bool_t _is_accessor_char(char c) { + return (c == '.'); +} + +/* Determine whether a character is numeric char */ +static bool_t _is_numeric_char(char c) { + return (c >= '0' && c <= '9') || _is_accessor_char(c); +} + +/* Determine whether a character is identifier char */ +static bool_t _is_identifier_char(char c) { + return ( + (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_') || + _is_numeric_char(c) || + (c == _STRING_POSTFIX_CHAR) + ); +} + +/* Determine whether a character is operator char */ +static bool_t _is_operator_char(char c) { + return ( + (c == '+') || (c == '-') || (c == '*') || (c == '/') || + (c == '^') || + (c == '(') || (c == ')') || + (c == '=') || + (c == '>') || (c == '<') + ); +} + +/* Determine whether a character is a exponential char */ +static bool_t _is_exponential_char(char c) { + return (c == 'e') || (c == 'E'); +} + +/* Determine whether a character is module using char */ +static bool_t _is_using_char(char c) { + return (c == '@'); +} + +/* Determine whether current symbol is exponent prefix */ +static bool_t _is_exponent_prefix(char* s, int begin, int end) { + int i = 0; + + mb_assert(s); + + if(end < 0) + return false; + + for(i = begin; i <= end; i++) { + if(!_is_numeric_char(s[i])) + return false; + } + + return true; +} + +/* Parse a character and append it to current parsing symbol */ +static int _append_char_to_symbol(mb_interpreter_t* s, char c) { + int result = MB_FUNC_OK; + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + + if(_is_accessor_char(c)) + context->current_symbol_contains_accessor++; + + if(context->current_symbol_nonius + 1 >= _SINGLE_SYMBOL_MAX_LENGTH) { + _set_current_error(s, SE_PS_SYMBOL_TOO_LONG, 0); + + result = MB_FUNC_ERR; + } else { + context->current_symbol[context->current_symbol_nonius] = c; + ++context->current_symbol_nonius; + } + + return result; +} + +#ifdef MB_ENABLE_UNICODE_ID +/* Parse a UTF8 character and append it to current parsing symbol */ +static int _append_uu_char_to_symbol(mb_interpreter_t* s, const char* str, int n) { + int result = MB_FUNC_OK; + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + + if(context->current_symbol_nonius + n >= _SINGLE_SYMBOL_MAX_LENGTH) { + _set_current_error(s, SE_PS_SYMBOL_TOO_LONG, 0); + + result = MB_FUNC_ERR; + } else { + memcpy(&context->current_symbol[context->current_symbol_nonius], str, n); + context->current_symbol_nonius += n; + } + + return result; +} +#endif /* MB_ENABLE_UNICODE_ID */ + +/* Cut current symbol when current one parsing is finished */ +static int _cut_symbol(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) { + int result = MB_FUNC_OK; + _parsing_context_t* context = 0; + char* sym = 0; + int status = 0; + bool_t delsym = false; + + mb_assert(s); + + context = s->parsing_context; + if(context->current_symbol_nonius && context->current_symbol[0] != _ZERO_CHAR) { + sym = (char*)mb_malloc(context->current_symbol_nonius + 1); + memcpy(sym, context->current_symbol, context->current_symbol_nonius + 1); + + status = _append_symbol(s, sym, &delsym, pos, row, col); + if(status || delsym) { + safe_free(sym); + } + result = status; + } + memset(context->current_symbol, 0, sizeof(context->current_symbol)); + context->current_symbol_nonius = 0; + context->current_symbol_contains_accessor = 0; + + return result; +} + +/* Append cut symbol to the AST list */ +static int _append_symbol(mb_interpreter_t* s, char* sym, bool_t* delsym, int pos, unsigned short row, unsigned short col) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _ls_node_t** assign = 0; + _ls_node_t* node = 0; + _parsing_context_t* context = 0; + + mb_assert(s && sym); + + ast = s->ast; + result = _create_symbol(s, ast, sym, &obj, &assign, delsym); + if(obj) { +#ifdef MB_ENABLE_SOURCE_TRACE + obj->source_pos = pos; + obj->source_row = row; + obj->source_col = col; +#else /* MB_ENABLE_SOURCE_TRACE */ + mb_unrefvar(row); + mb_unrefvar(col); + + obj->source_pos = (char)pos; +#endif /* MB_ENABLE_SOURCE_TRACE */ + + node = _ls_pushback(ast, obj); + if(assign) + *assign = node; + + context = s->parsing_context; + context->last_symbol = obj; + } + + return result; +} + +/* Create a syntax symbol */ +static int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** obj, _ls_node_t*** asgn, bool_t* delsym) { + int result = MB_FUNC_OK; + _data_e type; + union { + _func_t* func; _array_t* array; +#ifdef MB_ENABLE_CLASS + _class_t* instance; +#endif /* MB_ENABLE_CLASS */ + _routine_t* routine; _var_t* var; _label_t* label; real_t float_point; int_t integer; _raw_t any; + } tmp; + _raw_t value; + unsigned ul = 0; + _parsing_context_t* context = 0; + _running_context_t* running = 0; + _ls_node_t* glbsyminscope = 0; + bool_t is_field = false; + mb_unrefvar(l); + + mb_assert(s && sym && obj); + + memset(value, 0, sizeof(_raw_t)); + + context = s->parsing_context; + running = s->running_context; + + *obj = _create_object(); +#ifdef MB_ENABLE_SOURCE_TRACE + (*obj)->source_pos = -1; + (*obj)->source_row = (*obj)->source_col = 0xFFFF; +#else /* MB_ENABLE_SOURCE_TRACE */ + (*obj)->source_pos = -1; +#endif /* MB_ENABLE_SOURCE_TRACE */ + + type = _get_symbol_type(s, sym, &value); + if(s->last_error != SE_NO_ERR) { + result = MB_FUNC_ERR; + + goto _exit; + } + (*obj)->type = type; + switch(type) { + case _DT_NIL: + memcpy(tmp.any, value, sizeof(_raw_t)); + if(tmp.integer) { /* Nil type */ + (*obj)->type = _DT_NIL; + } else { /* End of line character */ + safe_free(*obj); + } + safe_free(sym); + + break; + case _DT_INT: + memcpy(tmp.any, value, sizeof(_raw_t)); + (*obj)->data.integer = tmp.integer; + safe_free(sym); + + break; + case _DT_REAL: + memcpy(tmp.any, value, sizeof(_raw_t)); + (*obj)->data.float_point = tmp.float_point; + safe_free(sym); + + break; + case _DT_STRING: { + size_t _sl = strlen(sym); + (*obj)->data.string = (char*)mb_malloc(_sl - 2 + 1); + memcpy((*obj)->data.string, sym + sizeof(char), _sl - 2); + (*obj)->data.string[_sl - 2] = _ZERO_CHAR; + *delsym = true; + } + + break; + case _DT_FUNC: + tmp.func = (_func_t*)mb_malloc(sizeof(_func_t)); + memset(tmp.func, 0, sizeof(_func_t)); + tmp.func->name = sym; + memcpy(&tmp.func->pointer, value, sizeof(tmp.func->pointer)); + (*obj)->data.func = tmp.func; + + break; + case _DT_ARRAY: + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ARRAY) { + (*obj)->data.array = ((_object_t*)glbsyminscope->data)->data.array; + (*obj)->ref = true; + *delsym = true; + } else { + tmp.array = _create_array(s, sym, _DT_UNKNOWN); + memcpy(&tmp.array->type, value, sizeof(tmp.array->type)); + (*obj)->data.array = tmp.array; + + ul = _ht_set_or_insert(running->var_dict, sym, *obj); + mb_assert(ul); + + *obj = _create_object(); + (*obj)->type = type; + (*obj)->data.array = tmp.array; + (*obj)->ref = true; + } + + break; +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { + (*obj)->data.instance = ((_object_t*)glbsyminscope->data)->data.instance; + (*obj)->ref = true; + *delsym = true; + if(running != (*obj)->data.instance->scope && + (context->class_state != _CLASS_STATE_NONE) && + _IS_FUNC(context->last_symbol, _core_class)) { + _push_scope_by_class(s, (*obj)->data.instance->scope); + } + } else { + _var_t* var = 0; + + tmp.instance = (_class_t*)mb_malloc(sizeof(_class_t)); + _init_class(s, tmp.instance, sym); + _push_scope_by_class(s, tmp.instance->scope); + s->last_instance = tmp.instance; + + mb_unrefvar(var); + + (*obj)->data.instance = tmp.instance; + + ul = _ht_set_or_insert(running->var_dict, sym, *obj); + mb_assert(ul); + + *obj = _create_object(); + (*obj)->type = type; + (*obj)->data.instance = tmp.instance; + (*obj)->ref = true; + } + + break; +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ROUTINE) { + (*obj)->data.routine = ((_object_t*)glbsyminscope->data)->data.routine; + (*obj)->ref = true; + *delsym = true; + if(running != (*obj)->data.routine->func.basic.scope && + context->routine_state && + _IS_FUNC(context->last_symbol, _core_def)) { + _push_scope_by_routine(s, (*obj)->data.routine->func.basic.scope); + } + } else { + _running_context_t* tba = 0; + tmp.routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); + _init_routine(s, tmp.routine, sym, 0); + _push_scope_by_routine(s, tmp.routine->func.basic.scope); + (*obj)->data.routine = tmp.routine; + + tba = _get_scope_to_add_routine(s); + ul = _ht_set_or_insert(tba->var_dict, sym, *obj); + mb_assert(ul); + if(tba != _OUTTER_SCOPE(running) && tba != running) + _pop_scope(s, false); + + *obj = _create_object(); + (*obj)->type = type; + (*obj)->data.routine = tmp.routine; + (*obj)->ref = true; + +#ifdef MB_ENABLE_CLASS + tmp.routine->instance = s->last_instance; +#endif /* MB_ENABLE_CLASS */ + } + + break; + case _DT_VAR: + if(context->routine_params_state) + glbsyminscope = _ht_find(running->var_dict, sym); + else + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); +#ifdef MB_ENABLE_CLASS + is_field = context->last_symbol && _IS_FUNC(context->last_symbol, _core_var); +#endif /* MB_ENABLE_CLASS */ + if(!is_field && glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { + (*obj)->data.variable = ((_object_t*)glbsyminscope->data)->data.variable; + (*obj)->ref = true; + *delsym = true; + } else { +#ifdef MB_ENABLE_CLASS + if(strcmp(sym, _CLASS_ME) == 0) { + _handle_error_now(s, SE_RN_CANNOT_CHANGE_ME, s->source_file, MB_FUNC_ERR); + (*obj)->ref = true; + *delsym = true; + + goto _exit; + } +#endif /* MB_ENABLE_CLASS */ + tmp.var = (_var_t*)mb_malloc(sizeof(_var_t)); + memset(tmp.var, 0, sizeof(_var_t)); + tmp.var->name = sym; + tmp.var->data = _create_object(); + tmp.var->data->type = (sym[strlen(sym) - 1] == _STRING_POSTFIX_CHAR) ? _DT_STRING : _DT_INT; + tmp.var->data->data.integer = 0; +#ifdef MB_ENABLE_CLASS + if(context->class_state != _CLASS_STATE_NONE) + tmp.var->pathing = true; + else if(!is_field) + tmp.var->pathing = !!context->current_symbol_contains_accessor; +#endif /* MB_ENABLE_CLASS */ + (*obj)->data.variable = tmp.var; + + ul = _ht_set_or_insert(running->var_dict, sym, *obj); + mb_assert(ul); + + *obj = _create_object(); + (*obj)->type = type; + (*obj)->data.variable = tmp.var; + (*obj)->ref = true; + } + + break; + case _DT_LABEL: + if(context->current_char == ':') { + if(mb_memtest(value, sizeof(_raw_t))) { + memcpy(&((*obj)->data.label), value, sizeof((*obj)->data.label)); + (*obj)->ref = true; + *delsym = true; + } else { + tmp.label = (_label_t*)mb_malloc(sizeof(_label_t)); + memset(tmp.label, 0, sizeof(_label_t)); + tmp.label->name = sym; + *asgn = &(tmp.label->node); + (*obj)->data.label = tmp.label; + + ul = _ht_set_or_insert(running->var_dict, sym, *obj); + mb_assert(ul); + + *obj = _create_object(); + (*obj)->type = type; + (*obj)->data.label = tmp.label; + (*obj)->ref = true; + } + } else { + (*obj)->data.label = (_label_t*)mb_malloc(sizeof(_label_t)); + memset((*obj)->data.label, 0, sizeof(_label_t)); + (*obj)->data.label->name = sym; + } + + break; + case _DT_SEP: + (*obj)->data.separator = sym[0]; + safe_free(sym); + + break; + case _DT_EOS: + safe_free(sym); + + break; + default: /* Do nothing */ + break; + } + +_exit: + return result; +} + +/* Get the type of a syntax symbol */ +static _data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) { + _data_e result = _DT_NIL; + union { real_t float_point; int_t integer; _object_t* obj; _raw_t any; } tmp; + char* conv_suc = 0; + _parsing_context_t* context = 0; + _running_context_t* running = 0; + _ls_node_t* glbsyminscope = 0; + size_t _sl = 0; + _data_e en = _DT_UNKNOWN; + intptr_t ptr = 0; + bool_t mod = false; + + mb_assert(s && sym); + _sl = strlen(sym); + mb_assert(_sl > 0); + + context = s->parsing_context; + running = s->running_context; + + /* int_t */ + tmp.integer = (int_t)mb_strtol(sym, &conv_suc, 0); + if(*conv_suc == _ZERO_CHAR) { + memcpy(*value, tmp.any, sizeof(_raw_t)); + + result = _DT_INT; + + goto _exit; + } + /* real_t */ + tmp.float_point = (real_t)mb_strtod(sym, &conv_suc); + if(*conv_suc == _ZERO_CHAR) { + memcpy(*value, tmp.any, sizeof(_raw_t)); + + result = _DT_REAL; + + goto _exit; + } + /* String */ + if(_is_quotation_char(sym[0]) && _is_quotation_char(sym[_sl - 1]) && _sl >= 2) { + result = _DT_STRING; + + if(context->last_symbol && _IS_FUNC(context->last_symbol, _core_import)) { + /* IMPORT statement */ + int n = context->current_symbol_nonius; + char current_symbol[_SINGLE_SYMBOL_MAX_LENGTH + 1]; + char* buf = 0; + memcpy(current_symbol, context->current_symbol, sizeof(current_symbol)); + memset(context->current_symbol, 0, sizeof(current_symbol)); + context->current_symbol_nonius = 0; + context->last_symbol = 0; + sym[_sl - 1] = _ZERO_CHAR; + context->parsing_state = _PS_NORMAL; + /* Using a module */ + if(_is_using_char(*(sym + 1))) { +#ifdef MB_ENABLE_MODULE + char* ns = mb_strdup(sym + 2, strlen(sym + 2) + 1); + if(_ls_find(s->using_modules, ns, (_ls_compare)_ht_cmp_string, 0)) { + safe_free(ns); + } else { + _ls_pushback(s->using_modules, ns); + } + + goto _end_import; +#else /* MB_ENABLE_MODULE */ + _handle_error_now(s, SE_CM_NOT_SUPPORTED, s->source_file, MB_FUNC_ERR); + + goto _end_import; +#endif /* MB_ENABLE_MODULE */ + } + /* Import another file */ + buf = _load_file(s, sym + 1, ":"); + if(buf) { + if(buf != sym + 1) { + char* lf = (char*)(_ls_back(context->imported)->data); + int pos; unsigned short row, col; + lf = _prev_import(s, lf, &pos, &row, &col); + mb_load_string(s, buf, true); + safe_free(buf); + _post_import(s, lf, &pos, &row, &col); + } + } else { + if(!_ls_find(context->imported, (void*)(sym + 1), (_ls_compare)_ht_cmp_string, 0)) { + if(s->import_handler) { + _object_t* sep = _create_object(); + sep->type = _DT_SEP; + sep->data.separator = ':'; + _ls_pushback(s->ast, sep); + if(s->import_handler(s, sym + 1) == MB_FUNC_OK) { + _ls_pushback(context->imported, mb_strdup(sym + 1, strlen(sym + 1) + 1)); + } else { + _handle_error_now(s, SE_PS_OPEN_FILE_FAILED, s->source_file, MB_FUNC_ERR); + } + } else { + _handle_error_now(s, SE_PS_OPEN_FILE_FAILED, s->source_file, MB_FUNC_ERR); + } + } + } + +_end_import: + context->parsing_state = _PS_STRING; + sym[_sl - 1] = '"'; + context->current_symbol_nonius = n; + memcpy(context->current_symbol, current_symbol, sizeof(current_symbol)); + result = _DT_NIL; + } + + goto _exit; + } + /* Nil */ + if(!strcmp(sym, MB_NIL)) { + tmp.integer = ~0; + memcpy(*value, tmp.any, sizeof(_raw_t)); + + result = _DT_NIL; + + goto _exit; + } + /* REM */ + if(!strcmp(sym, _REMARK_STR)) { + context->parsing_state = _PS_COMMENT; + + result = _DT_EOS; + + goto _exit; + } + /* _array_t */ + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ARRAY) { + tmp.obj = (_object_t*)glbsyminscope->data; + memcpy(*value, &(tmp.obj->data.array->type), sizeof(tmp.obj->data.array->type)); + + result = _DT_ARRAY; + + goto _exit; + } + if(context->last_symbol && _IS_FUNC(context->last_symbol, _core_dim)) { +#ifdef MB_SIMPLE_ARRAY + en = (sym[_sl - 1] == _STRING_POSTFIX_CHAR ? _DT_STRING : _DT_REAL); +#else /* MB_SIMPLE_ARRAY */ + en = _DT_REAL; +#endif /* MB_SIMPLE_ARRAY */ + memcpy(*value, &en, sizeof(en)); + + result = _DT_ARRAY; + + goto _exit; + } + /* _class_t */ +#ifdef MB_ENABLE_CLASS + if(context->last_symbol) { + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { + if(_IS_FUNC(context->last_symbol, _core_class)) { + _handle_error_now(s, SE_RN_DUPLICATE_CLASS, s->source_file, MB_FUNC_ERR); + } + result = _DT_CLASS; + + goto _exit; + } + if(_IS_FUNC(context->last_symbol, _core_class)) { + if(s->last_instance) { + _handle_error_now(s, SE_RN_DUPLICATE_CLASS, s->source_file, MB_FUNC_ERR); + + goto _exit; + } + _begin_class(s); +# ifdef MB_ENABLE_UNICODE_ID + if(!_is_identifier_char(sym[0]) && !mb_uu_ischar(sym)) { +# else /* MB_ENABLE_UNICODE_ID */ + if(!_is_identifier_char(sym[0])) { +# endif /* MB_ENABLE_UNICODE_ID */ + result = _DT_NIL; + + goto _exit; + } + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { + _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); + + goto _exit; + } + + if(context->routine_state > 1) { + _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); + + goto _exit; + } + + result = _DT_CLASS; + + goto _exit; + } else if(_IS_FUNC(context->last_symbol, _core_endclass)) { + if(_end_class(s)) + _pop_scope(s, false); + } + } +#endif /* MB_ENABLE_CLASS */ + /* _routine_t */ + if(context->last_symbol && !_is_bracket_char(sym[0])) { + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ROUTINE) { + if(_IS_FUNC(context->last_symbol, _core_def)) { + if(_begin_routine(s) != MB_FUNC_OK) + goto _exit; + } + result = _DT_ROUTINE; + + goto _exit; + } + if(_IS_FUNC(context->last_symbol, _core_def) || _IS_FUNC(context->last_symbol, _core_call)) { + if(_IS_FUNC(context->last_symbol, _core_def)) { + if(_begin_routine(s) != MB_FUNC_OK) + goto _exit; + } +#ifdef MB_ENABLE_UNICODE_ID + if(!_is_identifier_char(sym[0]) && !mb_uu_ischar(sym)) { +#else /* MB_ENABLE_UNICODE_ID */ + if(!_is_identifier_char(sym[0])) { +#endif /* MB_ENABLE_UNICODE_ID */ + result = _DT_NIL; + + goto _exit; + } + if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_VAR) { + _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); + + goto _exit; + } + + if(_IS_FUNC(context->last_symbol, _core_def)) { + if(context->routine_state > 1) { + _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); + + goto _exit; + } + } + + result = _DT_ROUTINE; + + goto _exit; + } else if(_IS_FUNC(context->last_symbol, _core_enddef)) { + if(_end_routine(s)) + _pop_scope(s, false); + } + } + /* _func_t */ + 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) { + ptr = (intptr_t)_core_neg; + memcpy(*value, &ptr, sizeof(intptr_t)); + + result = _DT_FUNC; + + goto _exit; + } + } + glbsyminscope = _find_func(s, sym, &mod); + if(glbsyminscope) { + if(context->last_symbol && context->last_symbol->type == _DT_ROUTINE) { + if(_sl == 1 && sym[0] == '(') + _begin_routine_parameter_list(s); + } + if(context->routine_params_state) { + if(_sl == 1 && sym[0] == ')') + _end_routine_parameter_list(s); + } + +#ifdef MB_ENABLE_MODULE + if(mod) { + _module_func_t* mp = (_module_func_t*)glbsyminscope->data; + ptr = (intptr_t)mp->func; + memcpy(*value, &ptr, sizeof(intptr_t)); + } else { + ptr = (intptr_t)glbsyminscope->data; + memcpy(*value, &ptr, sizeof(intptr_t)); + } +#else /* MB_ENABLE_MODULE */ + ptr = (intptr_t)glbsyminscope->data; + memcpy(*value, &ptr, sizeof(intptr_t)); +#endif /* MB_ENABLE_MODULE */ + + result = _DT_FUNC; + + goto _exit; + } + /* MB_EOS */ + if(_sl == 1 && sym[0] == MB_EOS) { + if(_IS_EOS(context->last_symbol)) + result = _DT_NIL; + else + result = _DT_EOS; + + goto _exit; + } + /* Separator */ + if(_sl == 1 && _is_separator_char(sym[0])) { + result = _DT_SEP; + + goto _exit; + } + /* _var_t */ + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope) { + if(((_object_t*)glbsyminscope->data)->type != _DT_LABEL) { + memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); + + result = _DT_VAR; + + goto _exit; + } + } + /* _label_t */ + if(context->current_char == ':') { + if(!context->last_symbol || _IS_EOS(context->last_symbol)) { + glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, _PATHING_NONE, 0, 0); + if(glbsyminscope) + memcpy(*value, &glbsyminscope->data, sizeof(glbsyminscope->data)); + + result = _DT_LABEL; + + goto _exit; + } + } + if(context->last_symbol && (_IS_FUNC(context->last_symbol, _core_goto) || _IS_FUNC(context->last_symbol, _core_gosub))) { + result = _DT_LABEL; + + goto _exit; + } + /* Otherwise */ + result = _DT_VAR; + +_exit: + return result; +} + +/* Parse a character */ +static int _parse_char(mb_interpreter_t* s, const char* str, int n, int pos, unsigned short row, unsigned short col) { + int result = MB_FUNC_OK; + _parsing_context_t* context = 0; + char last_char = _ZERO_CHAR; + char c = '\0'; +#ifdef MB_ENABLE_UNICODE_ID + unsigned uc = 0; +#else /* MB_ENABLE_UNICODE_ID */ + mb_unrefvar(n); +#endif /* MB_ENABLE_UNICODE_ID */ + + mb_assert(s && s->parsing_context); + + context = s->parsing_context; + + if(str) { +#ifdef MB_ENABLE_UNICODE_ID + if(n == 1) + c = *str; + else + memcpy(&uc, str, n); +#else /* MB_ENABLE_UNICODE_ID */ + c = *str; +#endif /* MB_ENABLE_UNICODE_ID */ + } else { + c = MB_EOS; + } + + last_char = context->current_char; + context->current_char = c; + + switch(context->parsing_state) { + case _PS_NORMAL: +#ifdef MB_ENABLE_UNICODE_ID + if(uc) { + if(context->symbol_state == _SS_IDENTIFIER) { + _mb_check(result = _append_uu_char_to_symbol(s, str, n), _exit); + } else if(context->symbol_state == _SS_OPERATOR) { + context->symbol_state = _SS_IDENTIFIER; + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_uu_char_to_symbol(s, str, n), _exit); + } + + break; + } +#endif /* MB_ENABLE_UNICODE_ID */ + + c = toupper(c); + if(_is_blank_char(c)) { /* \t space */ + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + } else if(_is_newline_char(c)) { /* \r \n EOF */ + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, MB_EOS), _exit); + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + } else if(_is_separator_char(c) || _is_bracket_char(c)) { /* , ; : ( ) */ + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, c), _exit); + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + } else if(_is_quotation_char(c)) { /* " */ + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, c), _exit); + context->parsing_state = _PS_STRING; + } else if(_is_comment_char(c)) { /* ' */ + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, MB_EOS), _exit); + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + context->parsing_state = _PS_COMMENT; + context->multi_line_comment_count = 1; + } else { + if(context->symbol_state == _SS_IDENTIFIER) { + if(_is_identifier_char(c)) { + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } else if(_is_operator_char(c)) { + if(_is_exponent_prefix(context->current_symbol, 0, context->current_symbol_nonius - 2) && _is_exponential_char(last_char) && c == '-') { + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } else { + context->symbol_state = _SS_OPERATOR; + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } + } else { + _handle_error_at_pos(s, SE_PS_INVALID_CHAR, s->source_file, pos, row, col, MB_FUNC_ERR, _exit, result); + } + } else if(context->symbol_state == _SS_OPERATOR) { + if(_is_identifier_char(c)) { + context->symbol_state = _SS_IDENTIFIER; + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } else if(_is_operator_char(c)) { + if(c == '-') + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } else { + _handle_error_at_pos(s, SE_PS_INVALID_CHAR, s->source_file, pos, row, col, MB_FUNC_ERR, _exit, result); + } + } else { + mb_assert(0 && "Impossible."); + } + } + + break; + case _PS_STRING: + if(_is_quotation_char(c)) { /* " */ + _mb_check(result = _append_char_to_symbol(s, c), _exit); + _mb_check(result = _cut_symbol(s, pos, row, col), _exit); + context->parsing_state = _PS_NORMAL; + } else { + _mb_check(result = _append_char_to_symbol(s, c), _exit); + } + + break; + case _PS_COMMENT: + if(_is_eof_char(c)) { /* EOF */ + context->parsing_state = _PS_NORMAL; + + break; + } + if(context->multi_line_comment_count != 0 && c == _MULTI_LINE_COMMENT_PREFIX[context->multi_line_comment_count++]) { + if(context->multi_line_comment_count >= countof(_MULTI_LINE_COMMENT_PREFIX) - 1) { + context->parsing_state = _PS_MULTI_LINE_COMMENT; + context->multi_line_comment_count = 0; + + break; + } + + break; + } else { + context->multi_line_comment_count = 0; + } + if(_is_newline_char(c)) /* \r \n EOF */ + context->parsing_state = _PS_NORMAL; + + break; + case _PS_MULTI_LINE_COMMENT: + if(_is_eof_char(c)) { /* EOF */ + context->parsing_state = _PS_NORMAL; + + break; + } + if(_is_comment_char(c) && context->multi_line_comment_count == 0) { + context->multi_line_comment_count = 1; + } else if(context->multi_line_comment_count != 0 && c == _MULTI_LINE_COMMENT_POSTFIX[context->multi_line_comment_count++]) { + if(context->multi_line_comment_count >= countof(_MULTI_LINE_COMMENT_POSTFIX) - 1) { + context->parsing_state = _PS_NORMAL; + context->multi_line_comment_count = 0; + } + } else { + context->multi_line_comment_count = 0; + } + + break; + default: + mb_assert(0 && "Unknown parsing state."); + + break; + } + +_exit: + return result; +} + +/* Set the position of an error */ +static void _set_error_pos(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) { + mb_assert(s); + + s->last_error_pos = pos; + s->last_error_row = row; + s->last_error_col = col; +} + +/* Do something before importing another file */ +static char* _prev_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) { +#ifdef MB_ENABLE_SOURCE_TRACE + char* result = 0; + _parsing_context_t* context = 0; + _import_info_t* info = 0; + _object_t* obj = 0; + + mb_assert(s); + + context = s->parsing_context; + if(context) { + if(pos) *pos = context->parsing_pos; + if(row) *row = context->parsing_row; + if(col) *col = context->parsing_col; + context->parsing_pos = 0; + context->parsing_row = 1; + context->parsing_col = 0; + } + + result = s->source_file; + s->source_file = lf; + + obj = _create_object(); + obj->type = _DT_EOS; + obj->ref = false; + _ls_pushback(s->ast, obj); + + info = (_import_info_t*)mb_malloc(sizeof(_import_info_t)); + info->source_file = lf ? mb_strdup(lf, strlen(lf) + 1) : 0; + obj = _create_object(); + obj->type = _DT_PREV_IMPORT; + obj->ref = false; + obj->data.import_info = info; + _ls_pushback(s->ast, obj); + + return result; +#else /* MB_ENABLE_SOURCE_TRACE */ + mb_unrefvar(s); + mb_unrefvar(lf); + mb_unrefvar(pos); + mb_unrefvar(row); + mb_unrefvar(col); + + return 0; +#endif /* MB_ENABLE_SOURCE_TRACE */ +} + +/* Do something after importing another file */ +static char* _post_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) { +#ifdef MB_ENABLE_SOURCE_TRACE + char* result = 0; + _parsing_context_t* context = 0; + _import_info_t* info = 0; + _object_t* obj = 0; + + mb_assert(s); + + context = s->parsing_context; + if(context) { + if(pos) context->parsing_pos = *pos; + if(row) context->parsing_row = *row; + if(col) context->parsing_col = *col; + } + + s->source_file = lf; + result = s->source_file; + + info = (_import_info_t*)mb_malloc(sizeof(_import_info_t)); + info->source_file = lf ? mb_strdup(lf, strlen(lf) + 1) : 0; + obj = _create_object(); + obj->type = _DT_POST_IMPORT; + obj->ref = false; + obj->data.import_info = info; + _ls_pushback(s->ast, obj); + + return result; +#else /* MB_ENABLE_SOURCE_TRACE */ + mb_unrefvar(s); + mb_unrefvar(lf); + mb_unrefvar(pos); + mb_unrefvar(row); + mb_unrefvar(col); + + return 0; +#endif /* MB_ENABLE_SOURCE_TRACE */ +} + +/** Object processors */ + +/* Get the size of a data type */ +static int_t _get_size_of(_data_e type) { + int_t result = 0; + +#ifdef MB_SIMPLE_ARRAY + if(type == _DT_INT) { + result = sizeof(int_t); + } else if(type == _DT_REAL) { + result = sizeof(real_t); + } else if(type == _DT_STRING) { + result = sizeof(char*); + } else { + mb_assert(0 && "Unsupported."); + } +#else /* MB_SIMPLE_ARRAY */ + mb_unrefvar(type); + + result = sizeof(_raw_u); +#endif /* MB_SIMPLE_ARRAY */ + + return result; +} + +/* Try to get a value (typed as int_t, real_t or char*) */ +static bool_t _try_get_value(_object_t* obj, mb_value_u* val, _data_e expected) { + bool_t result = false; + + mb_assert(obj && val); + + if(obj->type == _DT_INT && (expected == _DT_UNKNOWN || expected == _DT_INT)) { + val->integer = obj->data.integer; + result = true; + } else if(obj->type == _DT_REAL && (expected == _DT_UNKNOWN || expected == _DT_REAL)) { + val->float_point = obj->data.float_point; + result = true; + } else if(obj->type == _DT_VAR) { + result = _try_get_value(obj->data.variable->data, val, expected); + } + + return result; +} + +/* Determine if an object is a number */ +static bool_t _is_number(void* obj) { + bool_t result = false; + _object_t* o = 0; + + mb_assert(obj); + + o = (_object_t*)obj; + if(o->type == _DT_INT || o->type == _DT_REAL) + result = true; + else if(o->type == _DT_VAR) + result = o->data.variable->data->type == _DT_INT || o->data.variable->data->type == _DT_REAL; + + return result; +} + +/* Determine if an object is a string value or a string variable */ +static bool_t _is_string(void* obj) { + bool_t result = false; + _object_t* o = 0; + + mb_assert(obj); + + o = (_object_t*)obj; + if(o->type == _DT_STRING) + result = true; + else if(o->type == _DT_VAR) + result = o->data.variable->data->type == _DT_STRING; + + return result; +} + +/* Extract a string from an object */ +static char* _extract_string(_object_t* obj) { + char* result = 0; + + mb_assert(obj); + + if(obj->type == _DT_STRING) + result = obj->data.string; + else if(obj->type == _DT_VAR && obj->data.variable->data->type == _DT_STRING) + result = obj->data.variable->data->data.string; + + if(!result) + result = MB_NULL_STRING; + + return result; +} + +#ifdef _HAS_REF_OBJ_LOCK +/* Lock a referenced object */ +static bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { + mb_assert(lk); + + _ref(ref, obj); + if(*lk >= 0) + ++(*lk); + else + --(*lk); + + return true; +} + +/* Unlock a referenced object */ +static bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { + bool_t result = true; + + mb_assert(lk); + + if(*lk > 0) + --(*lk); + else if(*lk < 0) + ++(*lk); + else + result = false; + _unref(ref, obj); + + return result; +} + +/* Write operation on a referenced object */ +static bool_t _write_on_ref_object(_lock_t* lk, _ref_t* ref, void* obj) { + bool_t result = true; + mb_unrefvar(ref); + mb_unrefvar(obj); + + mb_assert(lk); + + if(*lk > 0) + *lk = -(*lk); + else + result = false; + + return result; +} +#endif /* _HAS_REF_OBJ_LOCK */ + +/* Increase the reference of a stub by 1 */ +static _ref_count_t _ref(_ref_t* ref, void* data) { + _ref_count_t before = *ref->count; + mb_unrefvar(data); + + ++(*ref->count); + if(before > *ref->count) { + mb_assert(0 && "Too many referencing, count overflow, please redefine _ref_count_t."); + + _handle_error_now(ref->s, SE_RN_REFERENCE_COUNT_OVERFLOW, ref->s->last_error_file, MB_FUNC_ERR); + } + + return *ref->count; +} + +/* Decrease the reference of a stub by 1 */ +static bool_t _unref(_ref_t* ref, void* data) { + bool_t result = true; + _gc_t* gc = 0; + bool_t cld = false; + + cld = *ref->count == _NONE_REF + 1; + do { + gc = &ref->s->gc; + result = --(*ref->count) == _NONE_REF; + mb_assert(*ref->count >= _NONE_REF); + _gc_add(ref, data, &ref->s->gc); + if(ref->count && *ref->count == _NONE_REF) + _tidy_intermediate_value(ref, data); + ref->on_unref(ref, data); + if(result) + _gc_remove(ref, data, gc); + } while(0); + if(cld) { + _ht_set_or_insert(gc->collected_table, ref, data); + _ht_set_or_insert(gc->collected_table, data, ref); + } + + return result; +} + +/* Increase the weak reference of a stub by 1 */ +static _ref_count_t _weak_ref(_ref_t* ref, void* data, _ref_t* weak) { + _ref_count_t before = *ref->weak_count; + mb_unrefvar(data); + + ++(*ref->weak_count); + if(before > *ref->weak_count) { + mb_assert(0 && "Too many referencing, weak count overflow, please redefine _ref_count_t."); + + _handle_error_now(ref->s, SE_RN_WEAK_REFERENCE_COUNT_OVERFLOW, ref->s->last_error_file, MB_FUNC_ERR); + } + memcpy(weak, ref, sizeof(_ref_t)); + + return *ref->weak_count; +} + +/* Decrease the weak reference of a stub by 1 */ +static bool_t _weak_unref(_ref_t* weak) { + bool_t result = true; + + --(*weak->weak_count); + mb_assert(*weak->weak_count >= _NONE_REF); + if(weak->count && *weak->count == _NONE_REF) + result = false; + if(weak->count && *weak->count == _NONE_REF && weak->weak_count && *weak->weak_count == _NONE_REF) { + safe_free(weak->weak_count); + safe_free(weak->count); + } + + return result; +} + +/* Create a reference stub, initialize the reference count with zero */ +static void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpreter_t* s) { + if(ref->count) + return; + + ref->count = (_ref_count_t*)mb_malloc(sizeof(_ref_count_t)); + *ref->count = _NONE_REF; + ref->weak_count = (_ref_count_t*)mb_malloc(sizeof(_ref_count_t)); + *ref->weak_count = _NONE_REF; + ref->on_unref = dtor; + ref->type = t; + ref->s = s; +} + +/* Destroy a reference stub */ +static void _destroy_ref(_ref_t* ref) { + if(!ref->count || !ref->weak_count) + return; + + if(*ref->weak_count == _NONE_REF) { + safe_free(ref->weak_count); + safe_free(ref->count); + } + ref->on_unref = 0; +} + +/* Add a referenced object to GC table for later garbage detection */ +static void _gc_add(_ref_t* ref, void* data, _gc_t* gc) { + _ht_node_t* table = 0; + + mb_assert(ref && data); + + if(!ref->count) + return; + + if(gc && _ht_find(gc->collected_table, ref)) + _ht_remove(gc->collected_table, ref, 0); + + if(!gc->table) + return; + + if(gc->collecting) + table = gc->recursive_table; + else + table = gc->table; + + if(gc && gc->valid_table && _ht_find(gc->valid_table, ref)) + _ht_remove(table, ref, 0); + else if(ref->count && *ref->count > _NONE_REF) + _ht_set_or_insert(table, ref, data); + else + _ht_remove(table, ref, 0); +} + +/* Remove a referenced object from GC */ +static void _gc_remove(_ref_t* ref, void* data, _gc_t* gc) { + _ht_node_t* table = 0; + + mb_assert(ref && data && gc); + + if(gc->collecting) + table = gc->recursive_table; + else + table = gc->table; + + if(table) + _ht_remove(table, ref, 0); +} + +/* Get reachable objects */ +static int _gc_add_reachable(void* data, void* extra, void* h) { + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + _var_t* var = 0; + _ht_node_t* ht = 0; + + mb_assert(data && h); + + ht = (_ht_node_t*)h; + obj = (_object_t*)data; + if(_is_internal_object(obj)) + goto _exit; + switch(obj->type) { + case _DT_VAR: + var = (_var_t*)obj->data.variable; + _gc_add_reachable(var->data, extra, ht); + + break; +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + if(!_ht_find(ht, &obj->data.usertype_ref->ref)) + _ht_set_or_insert(ht, &obj->data.usertype_ref->ref, obj->data.usertype_ref); + + break; +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_ARRAY_REF + case _DT_ARRAY: + if(!_ht_find(ht, &obj->data.array->ref)) + _ht_set_or_insert(ht, &obj->data.array->ref, obj->data.array); + + break; +#endif /* MB_ENABLE_ARRAY_REF */ +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: + if(!_ht_find(ht, &obj->data.list->ref)) { + _ht_set_or_insert(ht, &obj->data.list->ref, obj->data.list); + _LS_FOREACH(obj->data.list->list, _do_nothing_on_object, _gc_add_reachable, ht); + } + + break; + case _DT_DICT: + if(!_ht_find(ht, &obj->data.dict->ref)) { + _ht_set_or_insert(ht, &obj->data.dict->ref, obj->data.dict); + _HT_FOREACH(obj->data.dict->dict, _do_nothing_on_object, _gc_add_reachable_both, ht); + } + + break; + case _DT_LIST_IT: + if(!_ht_find(ht, &obj->data.list_it->list->ref)) { + _ht_set_or_insert(ht, &obj->data.list_it->list->ref, obj->data.list_it->list); + _LS_FOREACH(obj->data.list_it->list->list, _do_nothing_on_object, _gc_add_reachable, ht); + } + + break; + case _DT_DICT_IT: + if(!_ht_find(ht, &obj->data.dict_it->dict->ref)) { + _ht_set_or_insert(ht, &obj->data.dict_it->dict->ref, obj->data.dict_it->dict); + _HT_FOREACH(obj->data.dict_it->dict->dict, _do_nothing_on_object, _gc_add_reachable, ht); + } + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + if(!_ht_find(ht, &obj->data.instance->ref)) { + _ht_set_or_insert(ht, &obj->data.instance->ref, obj->data.instance); + _traverse_class(obj->data.instance, _gc_add_reachable, _add_class_meta_reachable, _META_LIST_MAX_DEPTH, false, ht, 0); + } + + break; +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_LAMBDA + case _DT_ROUTINE: + if(obj->data.routine->type == _IT_LAMBDA) { + if(!_ht_find(ht, &obj->data.routine->func.lambda.ref)) + _ht_set_or_insert(ht, &obj->data.routine->func.lambda.ref, obj->data.routine); + } + + break; +#endif /* MB_ENABLE_LAMBDA */ + default: /* Do nothing */ + break; + } + +_exit: + return result; +} + +/* Get reachable objects from both key and value */ +static int _gc_add_reachable_both(void* data, void* extra, void* h) { + int result = _OP_RESULT_NORMAL; + + mb_assert(data && extra && h); + + _gc_add_reachable(extra, 0, h); + _gc_add_reachable(data, extra, h); + + return result; +} + +/* Get all reachable referenced objects */ +static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht) { + _running_context_t* running = 0; + _ht_node_t* global_scope = 0; + + mb_assert(s && ht); + + running = s->running_context; + while(running) { + global_scope = running->var_dict; + if(global_scope) { + _HT_FOREACH(global_scope, _do_nothing_on_object, _gc_add_reachable, ht); + } + + running = running->prev; + } +} + +/* Destroy only the capsule (wrapper) of an object, leave the data behind, and add it to GC if possible */ +static int _gc_destroy_garbage_in_list(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + _ADDGC(obj, gc); + safe_free(obj); + + return result; +} + +/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ +static int _gc_destroy_garbage_in_dict(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + + mb_assert(data); + + obj = (_object_t*)data; + _ADDGC(obj, gc); + safe_free(obj); + + obj = (_object_t*)extra; + _ADDGC(obj, gc); + safe_free(obj); + + return result; +} + +#ifdef MB_ENABLE_CLASS +/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ +static int _gc_destroy_garbage_in_class(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + if(obj->type == _DT_VAR) { + _gc_destroy_garbage_in_class(obj->data.variable->data, 0, gc); + safe_free(obj->data.variable->name); + safe_free(obj->data.variable); + } else { + _ADDGC(obj, gc); + } + safe_free(obj); + + return result; +} +#endif /* MB_ENABLE_CLASS */ + +#ifdef MB_ENABLE_LAMBDA +/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */ +static int _gc_destroy_garbage_in_lambda(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + if(obj->type == _DT_VAR) { +#ifdef MB_ENABLE_CLASS + if(_is_string(obj) && obj->data.variable->pathing) { + safe_free(obj->data.variable->data); + } else { + _gc_destroy_garbage_in_lambda(obj->data.variable->data, 0, gc); + } +#else /* MB_ENABLE_CLASS */ + _gc_destroy_garbage_in_lambda(obj->data.variable->data, 0, gc); +#endif /* MB_ENABLE_CLASS */ + safe_free(obj->data.variable->name); + safe_free(obj->data.variable); + } else { + _ADDGC(obj, gc); + } + safe_free(obj); + + return result; +} + +/* Collect garbage of outer scopes */ +static void _gc_destroy_garbage_in_outer_scope(_running_context_ref_t* p, _gc_t* gc) { + while(p) { + _running_context_ref_t* scope = p; + p = p->prev; + _HT_FOREACH(scope->scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_lambda, gc); + _ht_clear(scope->scope->var_dict); + } +} +#endif /* MB_ENABLE_LAMBDA */ + +/* Destroy a garbage */ +static int _gc_destroy_garbage(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_NORMAL; + _ref_t* ref = 0; + bool_t proc = true; +#ifdef MB_ENABLE_COLLECTION_LIB + _list_t* lst = 0; + _dict_t* dct = 0; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_LAMBDA + _routine_t* routine = 0; +#endif /* MB_ENABLE_LAMBDA */ + + mb_assert(data && extra); + + ref = (_ref_t*)extra; + if(_ht_find(gc->collected_table, ref)) { + proc = true; + + goto _exit; + } + switch(ref->type) { +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: + lst = (_list_t*)data; + _LS_FOREACH(lst->list, _do_nothing_on_object, _gc_destroy_garbage_in_list, gc); + _ls_clear(lst->list); + lst->count = 0; + + break; + case _DT_DICT: + dct = (_dict_t*)data; + _HT_FOREACH(dct->dict, _do_nothing_on_object, _gc_destroy_garbage_in_dict, gc); + _ht_clear(dct->dict); + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_LAMBDA + case _DT_ROUTINE: + routine = (_routine_t*)data; + if(routine->type == _IT_LAMBDA) { + _HT_FOREACH(routine->func.lambda.scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_lambda, gc); + _ht_clear(routine->func.lambda.scope->var_dict); + if(routine->func.lambda.outer_scope && !_ht_find(gc->collected_table, &routine->func.lambda.outer_scope->ref)) + _gc_destroy_garbage_in_outer_scope(routine->func.lambda.outer_scope, gc); + } + + break; +#endif /* MB_ENABLE_LAMBDA */ +#ifdef MB_ENABLE_LAMBDA + case _DT_OUTER_SCOPE: /* Fall through */ +#endif /* MB_ENABLE_LAMBDA */ +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: /* Fall through */ +#endif /* MB_ENABLE_USERTYPE_REF */ + case _DT_ARRAY: /* Do nothing */ + break; + default: + proc = false; + + break; + } + if(proc && ref->count) + _unref(ref, data); + +_exit: + if(proc) + result = _OP_RESULT_DEL_NODE; + + return result; +} + +#ifdef MB_ENABLE_CLASS +/* Destroy a class instance garbage */ +static int _gc_destroy_garbage_class(void* data, void* extra, _gc_t* gc) { + int result = _OP_RESULT_NORMAL; + _ref_t* ref = 0; + bool_t proc = true; + _class_t* instance = 0; + + mb_assert(data && extra); + + ref = (_ref_t*)extra; + if(_ht_find(gc->collected_table, ref)) { + proc = true; + + goto _exit; + } + switch(ref->type) { + case _DT_CLASS: + instance = (_class_t*)data; + _HT_FOREACH(instance->scope->var_dict, _do_nothing_on_object, _gc_destroy_garbage_in_class, gc); + _ht_clear(instance->scope->var_dict); + _ls_clear(instance->meta_list); +#ifdef MB_ENABLE_LAMBDA + if(instance->scope->refered_lambdas) { + _ls_destroy(instance->scope->refered_lambdas); + instance->scope->refered_lambdas = 0; + } +#endif /* MB_ENABLE_LAMBDA */ + + break; + default: + proc = false; + + break; + } + if(proc && ref->count) + _unref(ref, data); + +_exit: + if(proc) + result = _OP_RESULT_DEL_NODE; + + return result; +} +#endif /* MB_ENABLE_CLASS */ + +/* Swap active garbage table and recursive table */ +static void _gc_swap_tables(mb_interpreter_t* s) { + _ht_node_t* tmp = 0; + + mb_assert(s); + + tmp = s->gc.table; + s->gc.table = s->gc.recursive_table; + s->gc.recursive_table = tmp; +} + +/* Try trigger garbage collection */ +static void _gc_try_trigger(mb_interpreter_t* s) { + mb_assert(s); + + if(_ht_count(s->gc.table) >= MB_GC_GARBAGE_THRESHOLD) + _gc_collect_garbage(s, 1); +} + +/* Collect all garbage */ +static void _gc_collect_garbage(mb_interpreter_t* s, int depth) { + _ht_node_t* valid = 0; + _gc_t* gc = 0; + + mb_assert(s); + + gc = &s->gc; + + /* Avoid infinity loop */ + if(gc->collecting) + return; + gc->collecting++; + + /* Get reachable information */ + valid = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + if(depth != -1) + gc->valid_table = valid; + _gc_get_reachable(s, valid); + + /* Get unreachable information */ + _HT_FOREACH(valid, _do_nothing_on_object, _ht_remove_exist, gc->table); + + /* Collect garbage */ + do { +#ifdef MB_ENABLE_CLASS + _HT_FOREACH(gc->table, _do_nothing_on_object, _gc_destroy_garbage_class, &s->gc); +#endif /* MB_ENABLE_CLASS */ + _HT_FOREACH(gc->table, _do_nothing_on_object, _gc_destroy_garbage, &s->gc); + _ht_clear(gc->table); + if(gc->collecting > 1) + gc->collecting--; + + if(!depth || !_ht_count(gc->recursive_table)) + break; + + _gc_swap_tables(s); + gc->collecting++; + } while(1); + + /* Tidy */ + _ht_clear(gc->collected_table); + gc->valid_table = 0; + _ht_clear(valid); + _ht_destroy(valid); + gc->collecting--; + mb_assert(!gc->collecting); +} + +#ifdef MB_ENABLE_USERTYPE_REF +/* Create a referenced usertype */ +static _usertype_ref_t* _create_usertype_ref(mb_interpreter_t* s, void* val, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft) { + _usertype_ref_t* result = 0; + + mb_assert(s); + + result = (_usertype_ref_t*)mb_malloc(sizeof(_usertype_ref_t)); + memset(result, 0, sizeof(_usertype_ref_t)); + result->usertype = val; + result->dtor = un; + result->clone = cl; + result->hash = hs; + result->cmp = cp; + result->fmt = ft; + _create_ref(&result->ref, _unref_usertype_ref, _DT_USERTYPE_REF, s); + + return result; +} + +/* Destroy a referenced usertype */ +static void _destroy_usertype_ref(_usertype_ref_t* c) { + mb_assert(c); + + if(c->dtor) + c->dtor(c->ref.s, c->usertype); + if(c->calc_operators) { + safe_free(c->calc_operators); + } + _destroy_ref(&c->ref); + safe_free(c); +} + +/* Unreference a referenced usertype */ +static void _unref_usertype_ref(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_usertype_ref((_usertype_ref_t*)data); +} +#endif /* MB_ENABLE_USERTYPE_REF */ + +/* Create an array */ +static _array_t* _create_array(mb_interpreter_t* s, const char* n, _data_e t) { + _array_t* result = 0; + + mb_assert(s); + + result = (_array_t*)mb_malloc(sizeof(_array_t)); + memset(result, 0, sizeof(_array_t)); + result->type = t; + result->name = (char*)n; +#ifdef MB_ENABLE_ARRAY_REF + _create_ref(&result->ref, _unref_array, _DT_ARRAY, s); + _ref(&result->ref, result); +#else /* MB_ENABLE_ARRAY_REF */ + mb_unrefvar(s); +#endif /* MB_ENABLE_ARRAY_REF */ + + return result; +} + +#ifdef MB_ENABLE_ARRAY_REF +/* Destroy an array */ +static void _destroy_array(_array_t* arr) { + mb_assert(arr); + + _clear_array(arr); + if(arr->name) { + safe_free(arr->name); + } +#ifndef MB_SIMPLE_ARRAY + if(arr->types) { + safe_free(arr->types); + } +#endif /* MB_SIMPLE_ARRAY */ +#ifdef MB_ENABLE_ARRAY_REF + _destroy_ref(&arr->ref); +#endif /* MB_ENABLE_ARRAY_REF */ + safe_free(arr); +} +#endif /* MB_ENABLE_ARRAY_REF */ + +/* Initialize an array */ +static void _init_array(_array_t* arr) { + int elemsize = 0; + + mb_assert(arr); + +#ifdef MB_SIMPLE_ARRAY + elemsize = (int)_get_size_of(arr->type); +#else /* MB_SIMPLE_ARRAY */ + elemsize = (int)_get_size_of(_DT_UNKNOWN); +#endif /* MB_SIMPLE_ARRAY */ + mb_assert(arr->count > 0); + mb_assert(!arr->raw); + arr->raw = (void*)mb_malloc(elemsize * arr->count); + if(arr->raw) + memset(arr->raw, 0, elemsize * arr->count); +#ifndef MB_SIMPLE_ARRAY + arr->types = (_data_e*)mb_malloc(sizeof(_data_e) * arr->count); + if(arr->types) { + unsigned ul = 0; + for(ul = 0; ul < arr->count; ++ul) + arr->types[ul] = _DT_INT; + } +#endif /* MB_SIMPLE_ARRAY */ +} + +/* Clone an array */ +static _array_t* _clone_array(mb_interpreter_t* s, _array_t* arr) { + _array_t* result = 0; + unsigned index = 0; + mb_value_u val; + _data_e type = _DT_NIL; + + mb_assert(s && arr); + + result = _create_array(s, mb_strdup(arr->name, 0), arr->type); + result->count = arr->count; + result->dimension_count = arr->dimension_count; + memcpy(result->dimensions, arr->dimensions, sizeof(result->dimensions)); + _init_array(result); + for(index = 0; index < arr->count; index++) { + _get_array_elem(s, arr, index, &val, &type); + _set_array_elem(s, 0, result, index, &val, &type); + } + + return result; +} + +/* Calculate the true index of an array */ +static int _get_array_pos(mb_interpreter_t* s, _array_t* arr, int* d, int c) { + int result = 0; + int i = 0; + unsigned n = 0; + int f = 1; + + mb_assert(s && arr && d); + + if(c < 0 || c > arr->dimension_count) { + result = -1; + + goto _exit; + } + for(i = 0; i < c; i++) { + n = d[i]; + if(n >= arr->dimensions[i]) { + result = -1; + + goto _exit; + } + result += n * f; + f *= arr->dimensions[i]; + } + +_exit: + return result; +} + +/* Calculate the true index of an array, used when walking through an AST */ +static int _get_array_index(mb_interpreter_t* s, _ls_node_t** l, _object_t* c, unsigned* index, bool_t* literally) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* arr = 0; + _object_t* len = 0; + _object_t subscript; + _object_t* subscript_ptr = 0; + mb_value_u val; + int dcount = 0; + int f = 1; + unsigned idx = 0; + + mb_assert(s && l && index); + + subscript_ptr = &subscript; + _MAKE_NIL(subscript_ptr); + + if(literally) *literally = false; + + /* Array name */ + ast = *l; + if(!c && ast && _is_array(ast->data)) + c = (_object_t*)ast->data; + if(!_is_array(c)) { + _handle_error_on_obj(s, SE_RN_ARRAY_IDENTIFIER_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(((_object_t*)c)->type == _DT_ARRAY) + arr = (_object_t*)c; + else + arr = ((_object_t*)c)->data.variable->data; + /* = */ + if(literally && ast->next && _IS_FUNC((_object_t*)ast->next->data, _core_equal)) { + *literally = true; + + goto _exit; + } + /* ( */ + if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_FUNC || ((_object_t*)ast->next->data)->data.func->pointer != _core_open_bracket) { + _handle_error_on_obj( + s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, + (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, + MB_FUNC_ERR, _exit, result + ); + } + ast = ast->next; + /* Array subscript */ + if(!ast->next) { + _handle_error_on_obj(s, SE_RN_ARRAY_SUBSCRIPT_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + while(((_object_t*)ast->data)->type != _DT_FUNC || ((_object_t*)ast->data)->data.func->pointer != _core_close_bracket) { + /* Calculate an integer value */ + result = _calc_expression(s, &ast, &subscript_ptr); + if(result != MB_FUNC_OK) + goto _exit; + len = subscript_ptr; + if(!_try_get_value(len, &val, _DT_INT)) { + _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(val.integer < 0) { + _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(dcount + 1 > arr->data.array->dimension_count) { + _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if((unsigned)val.integer >= arr->data.array->dimensions[dcount]) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + idx += (unsigned)val.integer * f; + /* Comma? */ + if(_IS_SEP(ast->data, ',')) + ast = ast->next; + + f *= arr->data.array->dimensions[dcount]; + ++dcount; + } + *index = idx; + if(!arr->data.array->raw) { + _handle_error_on_obj(s, SE_RN_RANK_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + +_exit: + *l = ast; + + return result; +} + +/* Get the value of an element in an array */ +static bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type) { + bool_t result = true; + int_t elemsize = 0; + unsigned pos = 0; + void* rawptr = 0; + + mb_assert(s && arr && val && type); + mb_assert(index < arr->count); + + elemsize = _get_size_of(arr->type); + pos = (unsigned)(elemsize * index); + rawptr = (void*)((intptr_t)arr->raw + pos); + if(arr->type == _DT_REAL) { +#ifdef MB_SIMPLE_ARRAY + val->float_point = *((real_t*)rawptr); + *type = _DT_REAL; +#else /* MB_SIMPLE_ARRAY */ + _COPY_BYTES(val->bytes, *((mb_val_bytes_t*)rawptr)); + *type = arr->types[index]; +#endif /* MB_SIMPLE_ARRAY */ + } else if(arr->type == _DT_STRING) { + val->string = *((char**)rawptr); + *type = _DT_STRING; + } else { + mb_assert(0 && "Unsupported."); + } + + return result; +} + +/* Set the value of an element in an array */ +static int _set_array_elem(mb_interpreter_t* s, _ls_node_t* ast, _array_t* arr, unsigned index, mb_value_u* val, _data_e* type) { + int result = MB_FUNC_OK; + int_t elemsize = 0; + unsigned pos = 0; + void* rawptr = 0; + mb_unrefvar(ast); + + mb_assert(s && arr && val); + mb_assert(index < arr->count); + + elemsize = _get_size_of(arr->type); + pos = (unsigned)(elemsize * index); + rawptr = (void*)((intptr_t)arr->raw + pos); +#ifdef MB_SIMPLE_ARRAY + switch(*type) { + case _DT_INT: + *((real_t*)rawptr) = (real_t)val->integer; + + break; + case _DT_STRING: { + size_t _sl = 0; + if(arr->type != _DT_STRING) { + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + _sl = strlen(val->string); + *((char**)rawptr) = (char*)mb_malloc(_sl + 1); + memcpy(*((char**)rawptr), val->string, _sl + 1); + } + + break; + default: + _COPY_BYTES(*((mb_val_bytes_t*)rawptr), val->bytes); + + break; + } +#else /* MB_SIMPLE_ARRAY */ + switch(*type) { + case _DT_STRING: { + size_t _sl = 0; + _sl = strlen(val->string); + *((char**)rawptr) = (char*)mb_malloc(_sl + 1); + memcpy(*((char**)rawptr), val->string, _sl + 1); + arr->types[index] = _DT_STRING; + } + + break; + default: + _COPY_BYTES(*((mb_val_bytes_t*)rawptr), val->bytes); + arr->types[index] = *type; + + break; + } +#endif /* MB_SIMPLE_ARRAY */ + + goto _exit; /* Avoid an unreferenced label warning */ + +_exit: + return result; +} + +/* Clear an array */ +static void _clear_array(_array_t* arr) { + char* str = 0; + int_t elemsize = 0; + unsigned pos = 0; + void* rawptr = 0; + unsigned ul = 0; + + mb_assert(arr); + + if(arr->raw) { +#ifndef MB_SIMPLE_ARRAY + if(arr->type == _DT_REAL) { + for(ul = 0; ul < arr->count; ++ul) { + if(arr->types[ul] == _DT_STRING) { + elemsize = _get_size_of(arr->type); + pos = (unsigned)(elemsize * ul); + rawptr = (void*)((intptr_t)arr->raw + pos); + str = *((char**)rawptr); + if(str) { + safe_free(str); + } + } + } + } +#endif /* MB_SIMPLE_ARRAY */ + if(arr->type == _DT_STRING) { + for(ul = 0; ul < arr->count; ++ul) { + elemsize = _get_size_of(arr->type); + pos = (unsigned)(elemsize * ul); + rawptr = (void*)((intptr_t)arr->raw + pos); + str = *((char**)rawptr); + if(str) { + safe_free(str); + } + } + } + safe_free(arr->raw); + } +} + +/* Determine if an object is an array value or an array variable */ +static bool_t _is_array(void* obj) { + bool_t result = false; + _object_t* o = 0; + + o = (_object_t*)obj; + if(o && o->type == _DT_ARRAY) + result = true; + else if(o && o->type == _DT_VAR) + result = o->data.variable->data->type == _DT_ARRAY; + + return result; +} + +#ifdef MB_ENABLE_ARRAY_REF +/* Unreference an array */ +static void _unref_array(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_array((_array_t*)data); +} +#endif /* MB_ENABLE_ARRAY_REF */ + +#ifdef MB_ENABLE_COLLECTION_LIB +/* Create a list */ +static _list_t* _create_list(mb_interpreter_t* s) { + _list_t* result = 0; + + mb_assert(s); + + result = (_list_t*)mb_malloc(sizeof(_list_t)); + memset(result, 0, sizeof(_list_t)); + result->list = _ls_create(); + _create_ref(&result->ref, _unref_list, _DT_LIST, s); + + return result; +} + +/* Destroy a list */ +static void _destroy_list(_list_t* c) { + mb_assert(c); + + if(c->range_begin) { safe_free(c->range_begin); } + _ls_foreach(c->list, _destroy_object); + _ls_destroy(c->list); + _destroy_ref(&c->ref); + safe_free(c); +} + +/* Create a dictionary */ +static _dict_t* _create_dict(mb_interpreter_t* s) { + _dict_t* result = 0; + + mb_assert(s); + + result = (_dict_t*)mb_malloc(sizeof(_dict_t)); + memset(result, 0, sizeof(_dict_t)); + result->dict = _ht_create(0, _ht_cmp_object, _ht_hash_object, _destroy_object_with_extra); + _create_ref(&result->ref, _unref_dict, _DT_DICT, s); + + return result; +} + +/* Destroy a dictionary */ +static void _destroy_dict(_dict_t* c) { + mb_assert(c); + + _ht_foreach(c->dict, _destroy_object_with_extra); + _ht_destroy(c->dict); + _destroy_ref(&c->ref); + safe_free(c); +} + +/* Create an iterator of a list */ +static _list_it_t* _create_list_it(_list_t* coll, bool_t lock) { + _list_it_t* result = 0; + + mb_assert(coll); + + result = (_list_it_t*)mb_malloc(sizeof(_list_it_t)); + memset(result, 0, sizeof(_list_it_t)); + result->list = coll; + result->locking = lock; + if(coll->range_begin) + result->curr.ranging = *coll->range_begin - sgn(coll->count); + else + result->curr.node = coll->list; + if(lock) + _lock_ref_object(&coll->lock, &coll->ref, coll); + _weak_ref(&coll->ref, coll, &result->weak_ref); + + return result; +} + +/* Destroy an iterator of a list */ +static bool_t _destroy_list_it(_list_it_t* it) { + bool_t result = true; + + mb_assert(it); + + if(_weak_unref(&it->weak_ref)) + _unlock_ref_object(&it->list->lock, &it->list->ref, it->list); + safe_free(it); + + return result; +} + +/* Move an iterator of a list to next step */ +static _list_it_t* _move_list_it_next(_list_it_t* it) { + _list_it_t* result = 0; + + if(!it || !it->list || !it->list->list) + goto _exit; + + if(it->list->lock < 0) { /* The iterator goes invalid if collection has been changed after obtaining iterator */ + result = it; + + goto _exit; + } + + if(!it->curr.node && !it->list->range_begin) + goto _exit; + + if(it->list->range_begin) { + if(it->list->lock) + it->curr.ranging += sgn(it->list->count); + + if(it->list->count > 0 && it->curr.ranging < *it->list->range_begin + it->list->count) + result = it; + else if(it->list->count < 0 && it->curr.ranging > *it->list->range_begin + it->list->count) + result = it; + } else { + if(it->list->lock) + it->curr.node = it->curr.node->next; + + if(it->curr.node) + result = it; + } + +_exit: + return result; +} + +/* Create an iterator of a dictionary */ +static _dict_it_t* _create_dict_it(_dict_t* coll, bool_t lock) { + _dict_it_t* result = 0; + + mb_assert(coll); + + result = (_dict_it_t*)mb_malloc(sizeof(_dict_it_t)); + memset(result, 0, sizeof(_dict_it_t)); + result->dict = coll; + result->locking = lock; + result->curr_bucket = 0; + result->curr_node = _INVALID_DICT_IT; + if(lock) + _lock_ref_object(&coll->lock, &coll->ref, coll); + _weak_ref(&coll->ref, coll, &result->weak_ref); + + return result; +} + +/* Destroy an iterator of a dictionary */ +static bool_t _destroy_dict_it(_dict_it_t* it) { + bool_t result = true; + + mb_assert(it); + + if(_weak_unref(&it->weak_ref)) + _unlock_ref_object(&it->dict->lock, &it->dict->ref, it->dict); + safe_free(it); + + return result; +} + +/* Move an iterator of a dictionary to next step */ +static _dict_it_t* _move_dict_it_next(_dict_it_t* it) { + _dict_it_t* result = 0; + + if(!it || !it->dict || !it->dict->dict || !it->curr_node) + goto _exit; + + if(!it->dict->lock) + goto _exit; + + if(it->curr_node && it->curr_node != _INVALID_DICT_IT) { + it->curr_node = it->curr_node->next; + if(!it->curr_node) + ++it->curr_bucket; + } + if(!it->curr_node || it->curr_node == _INVALID_DICT_IT) { + for( ; it->curr_bucket < it->dict->dict->array_size; ++it->curr_bucket) { + it->curr_node = it->dict->dict->array[it->curr_bucket]; + if(it->curr_node && it->curr_node->next) { + it->curr_node = it->curr_node->next; + + break; + } + } + } + + if(it->curr_node && it->curr_node->extra) + result = it; + +_exit: + return result; +} + +/* Unreference a list */ +static void _unref_list(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_list((_list_t*)data); +} + +/* Unreference a dictionary */ +static void _unref_dict(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_dict((_dict_t*)data); +} + +/* Push a value to a list */ +static void _push_list(_list_t* coll, mb_value_t* val, _object_t* oarg) { + mb_assert(coll && (val || oarg)); + + _fill_ranged(coll); + + if(val && !oarg) + _create_internal_object_from_public_value(val, &oarg); + _ls_pushback(coll->list, oarg); + coll->count++; + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); +} + +/* Pop a value from a list */ +static bool_t _pop_list(_list_t* coll, mb_value_t* val, mb_interpreter_t* s) { + _object_t* oval = 0; + + mb_assert(coll && val && s); + + _fill_ranged(coll); + + oval = (_object_t*)_ls_popback(coll->list); + if(oval) { + _internal_object_to_public_value(oval, val); + _destroy_object_capsule_only(oval, 0); + coll->count--; + + if(val->type == MB_DT_STRING) + _mark_lazy_destroy_string(s, val->value.string); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); + + return true; + } else { + mb_make_nil(*val); + + return false; + } +} + +/* Insert a value into a list */ +static bool_t _insert_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) { + _object_t* oarg = 0; + + mb_assert(coll && val); + + _fill_ranged(coll); + + _create_internal_object_from_public_value(val, &oarg); + if(oval) + *oval = oarg; + + if(_ls_insert_at(coll->list, (int)idx, oarg)) { + coll->count++; + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); + + return true; + } + + return false; +} + +/* Set an element in a list with a specific index with a given value */ +static bool_t _set_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) { + _ls_node_t* result = 0; + _object_t* oarg = 0; + + mb_assert(coll && (val || (oval && *oval))); + + _fill_ranged(coll); + + result = _node_at_list(coll, (int)idx); + if(result) { + if(result->data) + _destroy_object(result->data, 0); + if(val) { + _create_internal_object_from_public_value(val, &oarg); + if(oval) + *oval = oarg; + } else { + oarg = *oval; + } + result->data = oarg; + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); + } + + return !!(result && result->data); +} + +/* Remove an element in a list with a specific index */ +static bool_t _remove_at_list(_list_t* coll, int_t idx) { + bool_t result = false; + _ls_node_t* node = 0; + + mb_assert(coll); + + _fill_ranged(coll); + + node = _node_at_list(coll, (int)idx); + if(node) { + if(node->data) { + _ls_remove(coll->list, node, _destroy_object); + coll->count--; + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); + + result = true; + } + } + + return result; +} + +/* Get a node in a list with a specific index */ +static _ls_node_t* _node_at_list(_list_t* coll, int index) { + _ls_node_t* result = 0; + _ls_node_t* tmp = 0; + int n = 0; + + mb_assert(coll); + + _fill_ranged(coll); + + if(index >= 0 && index < (int)coll->count) { + /* Layout: HEAD ... LEFT ... PIVOT ... RIGHT ... TAIL + LEN(HEAD to LEFT) == LEN(LEFT to PIVOT) && LEN(PIVOT to RIGHT) == LEN(RIGHT to TAIL) + PIVOT is a cached node + */ + int head = 0, + left = coll->cached_index / 2, + right = coll->cached_index + (coll->count - coll->cached_index) / 2, + tail = coll->count - 1; + if(coll->cached_node) { + if(index >= head && index < left) { /* [HEAD, LEFT) */ + n = index; + tmp = coll->list->next; + while(tmp && n) { + tmp = tmp->next; + --n; + } + if(tmp) { + result = tmp; + coll->cached_node = tmp; + coll->cached_index = index; + } + } else if(index >= left && index <= right) { /* [LEFT, RIGHT] */ + while(index != coll->cached_index) { + if(index > coll->cached_index) { + coll->cached_node = coll->cached_node->next; + coll->cached_index++; + } else if(index < coll->cached_index) { + coll->cached_node = coll->cached_node->prev; + coll->cached_index--; + } + } + result = coll->cached_node; + } else if(index > right && index <= tail) { /* (RIGHT, TAIL] */ + n = tail - index; + tmp = coll->list->prev; + while(tmp && n) { + tmp = tmp->prev; + --n; + } + if(tmp) { + result = tmp; + coll->cached_node = tmp; + coll->cached_index = index; + } + } else { + mb_assert(0 && "Impossible."); + } + } else { + n = index; + tmp = coll->list->next; + while(tmp && n) { + tmp = tmp->next; + --n; + } + if(tmp) { + result = tmp; + coll->cached_node = tmp; + coll->cached_index = index; + } + } + } + + return result; +} + +/* Get the value in a list with a specific index */ +static bool_t _at_list(_list_t* coll, int_t idx, mb_value_t* oval) { + _ls_node_t* result = 0; + + mb_assert(coll && oval); + + _fill_ranged(coll); + + result = _node_at_list(coll, (int)idx); + if(oval && result && result->data) + _internal_object_to_public_value((_object_t*)result->data, oval); + + return !!(result && result->data); +} + +/* Find a value in a list */ +static bool_t _find_list(_list_t* coll, mb_value_t* val, int* idx) { + bool_t result = false; + _object_t* oarg = 0; + + mb_assert(coll && val); + + _fill_ranged(coll); + + _create_internal_object_from_public_value(val, &oarg); + result = !!_ls_find(coll->list, oarg, (_ls_compare)_ht_cmp_object, idx); + _destroy_object(oarg, 0); + + return result; +} + +/* Clear a list */ +static void _clear_list(_list_t* coll) { + mb_assert(coll); + + if(coll->range_begin) { safe_free(coll->range_begin); } + + _ls_foreach(coll->list, _destroy_object); + _ls_clear(coll->list); + coll->count = 0; + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); +} + +/* Sort a list */ +static void _sort_list(_list_t* coll) { + mb_assert(coll); + + _ls_sort(&coll->list, (_ls_compare)_ht_cmp_object); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); +} + +/* Invalidate cached list index */ +static void _invalidate_list_cache(_list_t* coll) { + mb_assert(coll); + + coll->cached_node = 0; + coll->cached_index = 0; +} + +/* Fill a ranged list with numbers */ +static void _fill_ranged(_list_t* coll) { + _object_t* obj = 0; + + mb_assert(coll); + + if(coll->range_begin) { + mb_value_t arg; + int_t begin = *coll->range_begin; + int_t end = *coll->range_begin + coll->count; + int_t step = sgn(coll->count); + + do { + mb_make_int(arg, begin); + _create_internal_object_from_public_value(&arg, &obj); + _ls_pushback(coll->list, obj); + + begin += step; + } while(begin != end); + safe_free(coll->range_begin); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + _invalidate_list_cache(coll); + } +} + +/* Set an element to a dictionary with a key-value pair */ +static void _set_dict(_dict_t* coll, mb_value_t* key, mb_value_t* val, _object_t* okey, _object_t* oval) { + _ls_node_t* exist = 0; + + mb_assert(coll && (key || okey) && (val || oval)); + + if(key && !okey) + _create_internal_object_from_public_value(key, &okey); + if(val && !oval) + _create_internal_object_from_public_value(val, &oval); + exist = _ht_find(coll->dict, okey); + if(exist) + _ht_remove(coll->dict, okey, _ls_cmp_extra_object); + _ht_set_or_insert(coll->dict, okey, oval); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); +} + +/* Remove an element to a dictionary with a specific key */ +static bool_t _remove_dict(_dict_t* coll, mb_value_t* key) { + _ls_node_t* result = 0; + _object_t* okey = 0; + void* data = 0; + void* extra = 0; + + mb_assert(coll && key); + + _create_internal_object_from_public_value(key, &okey); + result = _ht_find(coll->dict, okey); + if(result && result->data) { + data = result->data; + extra = result->extra; + _ht_remove(coll->dict, okey, _ls_cmp_extra_object); + _destroy_object(okey, 0); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); + } + + return !!(result && result->data); +} + +/* Find a key in a dictionary */ +static bool_t _find_dict(_dict_t* coll, mb_value_t* val, mb_value_t* oval) { + _ls_node_t* result = 0; + _object_t* oarg = 0; + + mb_assert(coll && val); + + _create_internal_object_from_public_value(val, &oarg); + result = _ht_find(coll->dict, oarg); + _destroy_object(oarg, 0); + if(oval) { + if(result && result->data) { + _internal_object_to_public_value((_object_t*)result->data, oval); + } else { + oval->type = MB_DT_UNKNOWN; + oval->value.integer = 0; + + return true; + } + } + + return !!(result && result->data); +} + +/* Clear a dictionary */ +static void _clear_dict(_dict_t* coll) { + mb_assert(coll); + + _ht_foreach(coll->dict, _destroy_object_with_extra); + _ht_clear(coll->dict); + + _write_on_ref_object(&coll->lock, &coll->ref, coll); +} + +/* Determin whether a list iterator is invalid */ +static bool_t _invalid_list_it(_list_it_t* it) { + if(!it) + return false; + + return it && it->list && it->list->lock <= 0; +} + +/* Determin whether a dictionary iterator is invalid */ +static bool_t _invalid_dict_it(_dict_it_t* it) { + if(!it) + return false; + + return it && it->dict && it->dict->lock <= 0; +} + +/* Assign an iterator to another object */ +static bool_t _assign_with_it(_object_t* tgt, _object_t* src) { + mb_assert(tgt && src); + + if(src->type != _DT_LIST_IT && src->type != _DT_DICT_IT) + return false; + + switch(src->type) { + case _DT_LIST_IT: + if(src->data.list_it->locking) { + tgt->data.list_it = _create_list_it(src->data.list_it->list, true); + } else { + tgt->data = src->data; + tgt->data.list_it->locking = true; + _lock_ref_object(&tgt->data.list_it->list->lock, &tgt->data.list_it->list->ref, tgt->data.list_it->list); + } + + break; + case _DT_DICT_IT: + if(src->data.dict_it->locking) { + tgt->data.dict_it = _create_dict_it(src->data.dict_it->dict, true); + } else { + tgt->data = src->data; + tgt->data.dict_it->locking = true; + _lock_ref_object(&tgt->data.dict_it->dict->lock, &tgt->data.dict_it->dict->ref, tgt->data.dict_it->dict); + } + + break; + default: + mb_assert(0 && "Impossible."); + + break; + } + + return true; +} + +/* Clone an object to a list */ +static int _clone_to_list(void* data, void* extra, _list_t* coll) { + _object_t* obj = 0; + _object_t* tgt = 0; + mb_unrefvar(extra); + + mb_assert(data && coll); + + _fill_ranged(coll); + + tgt = _create_object(); + obj = (_object_t*)data; + _clone_object(coll->ref.s, obj, tgt, false, false); + _push_list(coll, 0, tgt); + _REF(tgt) + + return 1; +} + +/* Clone a key-value pair to a dictionary */ +static int _clone_to_dict(void* data, void* extra, _dict_t* coll) { + _object_t* kobj = 0; + _object_t* ktgt = 0; + _object_t* vobj = 0; + _object_t* vtgt = 0; + + mb_assert(data && extra && coll); + + ktgt = _create_object(); + kobj = (_object_t*)extra; + _clone_object(coll->ref.s, kobj, ktgt, false, false); + + vtgt = _create_object(); + vobj = (_object_t*)data; + _clone_object(coll->ref.s, vobj, vtgt, false, false); + + _set_dict(coll, 0, 0, ktgt, vtgt); + _REF(ktgt) + _REF(vtgt) + + return 1; +} + +/* Copy an object from a list to an array */ +static int _copy_list_to_array(void* data, void* extra, _array_helper_t* h) { + _object_t* obj = 0; + mb_value_t val; + _data_e type = _DT_NIL; + mb_unrefvar(extra); + + mb_assert(data && h); + + obj = (_object_t*)data; + mb_make_nil(val); + _internal_object_to_public_value(obj, &val); + type = obj->type; + _set_array_elem(h->s, 0, h->array, h->index++, &val.value, &type); + + return 1; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifdef MB_ENABLE_CLASS +/* Initialize a class */ +static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n) { + _running_context_t* running = 0; + _object_t* meobj = 0; + _var_t* me = 0; + + mb_assert(s && instance && n); + + running = s->running_context; + + memset(instance, 0, sizeof(_class_t)); + _create_ref(&instance->ref, _unref_class, _DT_CLASS, s); + _ref(&instance->ref, instance); + instance->name = n; + instance->meta_list = _ls_create(); + instance->scope = _create_running_context(true); + instance->created_from = instance; + + me = _create_var(&meobj, _CLASS_ME, strlen(_CLASS_ME) + 1, true); + me->data->type = _DT_CLASS; + me->data->data.instance = instance; + me->pathing = true; + me->isme = true; + _ht_set_or_insert(instance->scope->var_dict, me->name, meobj); +} + +/* Begin parsing a class */ +static void _begin_class(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + context->class_state = _CLASS_STATE_PROC; +} + +/* End parsing a class */ +static bool_t _end_class(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + if(context->routine_state) { + _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); + } + if(context->class_state == _CLASS_STATE_NONE) { + _handle_error_now(s, SE_RN_INVALID_CLASS, s->source_file, MB_FUNC_ERR); + + return false; + } + context->class_state = _CLASS_STATE_NONE; + s->last_instance = 0; + + return true; +} + +/* Unreference a class instance */ +static void _unref_class(_ref_t* ref, void* data) { + mb_assert(ref); + + if(ref->s->valid) + _out_of_scope(ref->s, ((_class_t*)data)->scope, (_class_t*)data, 0, false); + + if(*ref->count == _NONE_REF) + _destroy_class((_class_t*)data); +} + +/* Destroy a class instance */ +static void _destroy_class(_class_t* c) { + mb_assert(c); + + if(c->meta_list) { + _unlink_meta_class(c->ref.s, c); + _ls_destroy(c->meta_list); + } + if(c->scope->var_dict) { + _ht_foreach(c->scope->var_dict, _destroy_object); + _ht_destroy(c->scope->var_dict); + } +#ifdef MB_ENABLE_LAMBDA + if(c->scope->refered_lambdas) { + _ls_destroy(c->scope->refered_lambdas); + c->scope->refered_lambdas = 0; + } +#endif /* MB_ENABLE_LAMBDA */ + safe_free(c->scope); + _destroy_ref(&c->ref); + safe_free(c->name); + safe_free(c); +} + +/* Traverse all fields of a class instance, and its meta class instances recursively as well */ +static bool_t _traverse_class(_class_t* c, _class_scope_walker scope_walker, _class_meta_walker meta_walker, unsigned meta_depth, bool_t meta_walk_on_self, void* extra_data, void* ret) { + bool_t result = true; + _ls_node_t* node = 0; + _class_t* meta = 0; + + mb_assert(c); + + if(scope_walker) { + _HT_FOREACH(c->scope->var_dict, _do_nothing_on_object, scope_walker, extra_data); + } + if(meta_walk_on_self) { + if(meta_walker) { + result = meta_walker(c, extra_data, ret); + if(!result) + goto _exit; + } + } + node = c->meta_list ? c->meta_list->next : 0; + while(node) { + meta = (_class_t*)node->data; + if(meta_walker && meta_depth) { + result = meta_walker(meta, extra_data, ret); + if(!result) + break; + } + result = _traverse_class( + meta, + scope_walker, + meta_walker, meta_depth ? meta_depth - 1 : 0, + meta_walk_on_self, + extra_data, ret + ); + if(!result) + break; + node = node->next; + } + +_exit: + return result; +} + +/* Link a class instance to the meta list of another class instance */ +static bool_t _link_meta_class(mb_interpreter_t* s, _class_t* derived, _class_t* base) { + mb_assert(s && derived && base); + + if(_ls_find(derived->meta_list, base, (_ls_compare)_ht_cmp_intptr, 0)) { + _handle_error_now(s, SE_RN_WRONG_META_CLASS, s->source_file, MB_FUNC_ERR); + + return false; + } + + _ls_pushback(derived->meta_list, base); + _ref(&base->ref, base); + + return true; +} + +/* Unlink all meta class instances of a class instance */ +static void _unlink_meta_class(mb_interpreter_t* s, _class_t* derived) { + mb_assert(s && derived); + + _LS_FOREACH(derived->meta_list, _do_nothing_on_object, _unlink_meta_instance, derived); + _ls_clear(derived->meta_list); +} + +/* Unlink a meta class instance */ +static int _unlink_meta_instance(void* data, void* extra, _class_t* derived) { + _class_t* base = 0; + mb_unrefvar(extra); + + mb_assert(data && derived); + + base = (_class_t*)data; + _unref(&base->ref, base); + + return 0; +} + +/* Clone fields of a class instance to another */ +static int _clone_clsss_field(void* data, void* extra, void* n) { + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + _array_t* arr = 0; + _var_t* var = 0; + _routine_t* sub = 0; + _class_t* instance = (_class_t*)n; + _object_t* ret = 0; + mb_unrefvar(extra); + + mb_assert(data && n); + + obj = (_object_t*)data; + if(_is_internal_object(obj)) + goto _exit; + switch(obj->type) { + case _DT_VAR: + var = obj->data.variable; + if(!_IS_ME(var)) { + if(_ht_find(instance->scope->var_dict, var->name)) + break; + + ret = _duplicate_parameter(var, 0, instance->scope); + _clone_object(instance->ref.s, obj, ret->data.variable->data, false, var->data->type != _DT_CLASS); + } + + break; + case _DT_ARRAY: + arr = obj->data.array; + if(!_ht_find(instance->scope->var_dict, arr->name)) { + ret = _create_object(); + ret->type = _DT_ARRAY; + ret->ref = false; + _clone_object(instance->ref.s, obj, ret, false, false); + + _ht_set_or_insert(instance->scope->var_dict, ret->data.array->name, ret); + } + + break; + case _DT_ROUTINE: + sub = obj->data.routine; + if(!_ht_find(instance->scope->var_dict, sub->name)) { + _routine_t* routine = _clone_routine(sub, instance, false); + ret = _create_object(); + ret->type = _DT_ROUTINE; + ret->data.routine = routine; + ret->ref = false; + + _ht_set_or_insert(instance->scope->var_dict, obj->data.routine->name, ret); + } + + break; + default: /* Do nothing */ + break; + } + +_exit: + return result; +} + +/* Link meta class to a new instance */ +static bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret) { + _class_t* instance = (_class_t*)n; + mb_unrefvar(ret); + + mb_assert(meta && n); + + _link_meta_class(instance->ref.s, instance, meta); + + return true; +} + +/* Search for a meta function with a specific name and assign to a member field */ +static int _search_class_meta_function(mb_interpreter_t* s, _class_t* instance, const char* n, _routine_t* _mb_unaligned * f) { + _ls_node_t* node = 0; + + mb_assert(s); + + node = _search_identifier_in_class(s, instance, n, 0, 0); + if(f) *f = 0; + if(node) { + _object_t* obj = (_object_t*)node->data; + if(obj && _IS_ROUTINE(obj)) { + if(f) *f = obj->data.routine; + + return 1; + } + } + + return 0; +} + +/* Search for the HASH and COMPARE meta function for a class */ +static int _search_class_hash_and_compare_functions(mb_interpreter_t* s, _class_t* instance) { + mb_assert(s && instance); + + _search_class_meta_function(s, instance, _CLASS_HASH_FUNC, &instance->hash); + _search_class_meta_function(s, instance, _CLASS_COMPARE_FUNC, &instance->compare); + + if(!instance->hash && !instance->compare) { + return MB_FUNC_OK; + } else if(instance->hash && instance->compare) { + return MB_FUNC_OK; + } else { + instance->hash = 0; + instance->compare = 0; + + return MB_FUNC_WARNING; + } +} + +/* Detect whether a class instance is inherited from another */ +static bool_t _is_a_class(_class_t* instance, void* m, void* ret) { + _class_t* meta = (_class_t*)m; + bool_t* r = (bool_t*)ret; + bool_t is_a = false; + + mb_assert(instance && meta && ret); + + do { + if(instance == meta) { + is_a = true; + + break; + } + if(instance == instance->created_from) + break; + instance = instance->created_from; + } while(1); + + *r = is_a; + + return !(*r); +} + +/* Add a meta class instance to a GC reachable table */ +static bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret) { + _ht_node_t* htable = (_ht_node_t*)ht; + mb_unrefvar(ret); + + mb_assert(meta && ht); + + if(!_ht_find(htable, &meta->ref)) + _ht_set_or_insert(htable, &meta->ref, meta); + + return true; +} + +#ifdef MB_ENABLE_COLLECTION_LIB +/* Reflect each field of a class instance to a dictionary */ +static int _reflect_class_field(void* data, void* extra, void* d) { + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + _var_t* var = 0; + _routine_t* sub = 0; + _dict_t* coll = (_dict_t*)d; + mb_unrefvar(extra); + + mb_assert(data && d); + + obj = (_object_t*)data; + if(_is_internal_object(obj)) + goto _exit; + switch(obj->type) { + case _DT_VAR: + var = (_var_t*)obj->data.variable; + if(!_ht_find(coll->dict, var->name)) { + mb_value_t kv, vv; + mb_make_string(kv, var->name); + _internal_object_to_public_value(obj, &vv); + _set_dict(coll, &kv, &vv, 0, 0); + } + + break; + case _DT_ROUTINE: + sub = (_routine_t*)obj->data.routine; + if(!_ht_find(coll->dict, sub->name)) { + mb_value_t kv, vv; + mb_make_string(kv, sub->name); + mb_make_type(vv, _internal_type_to_public_type(obj->type)); + _set_dict(coll, &kv, &vv, 0, 0); + } + + break; + default: /* Do nothing */ + break; + } + +_exit: + return result; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* Call the TOSTRING function of a class instance to format it */ +static int _format_class_to_string(mb_interpreter_t* s, void** l, _class_t* instance, _object_t* out, bool_t* got_tostr) { + int result = MB_FUNC_OK; + + mb_assert(s && l && instance && out); + + _ls_node_t* tsn = _search_identifier_in_class(s, instance, _CLASS_TOSTRING_FUNC, 0, 0); + if(got_tostr) *got_tostr = false; + if(tsn) { + _object_t* tso = (_object_t*)tsn->data; + _ls_node_t* tmp = (_ls_node_t*)*l; + mb_value_t va[1]; + mb_make_nil(va[0]); + if(_eval_routine(s, &tmp, va, 1, tso->data.routine, _has_routine_fun_arg, _pop_routine_fun_arg) == MB_FUNC_OK) { + _MAKE_NIL(out); + _public_value_to_internal_object(&s->running_context->intermediate_value, out); + if(out->type == _DT_STRING) { + out->data.string = mb_strdup(out->data.string, strlen(out->data.string) + 1); + out->ref = false; + mb_make_nil(s->running_context->intermediate_value); + } else { + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + if(got_tostr) + *got_tostr = true; + } + } + +_exit: + return result; +} + +/* Reflect a class instance from a string */ +static _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg) { + _ls_node_t* cs = 0; + _object_t* c = 0; + + cs = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); + if(!cs) + return 0; + c = (_object_t*)cs->data; + if(!c) + return 0; + c = _GET_CLASS(c); + if(!c) + return 0; + if(arg) + _internal_object_to_public_value(c, arg); + + return c->data.instance; +} + +/* Detect whether it's accessing a member of a class instance following a sub routine */ +static bool_t _is_valid_class_accessor_following_routine(mb_interpreter_t* s, _var_t* var, _ls_node_t* ast, _ls_node_t** out) { + bool_t result = false; + _running_context_t* running = 0; + + mb_assert(s && var && ast); + + running = s->running_context; + + if(out) *out = 0; + + if(_is_accessor_char(*var->name) && (ast && ast->prev && _IS_FUNC(ast->prev->data, _core_close_bracket)) && running->intermediate_value.type == MB_DT_CLASS) { + _class_t* instance = (_class_t*)running->intermediate_value.value.instance; + _ls_node_t* fn = _search_identifier_in_class(s, instance, var->name + 1, 0, 0); + result = true; + if(fn && out) + *out = fn; + } + + return result; +} +#endif /* MB_ENABLE_CLASS */ + +/* Initialize a routine */ +static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f) { + _running_context_t* running = 0; + + mb_assert(s && routine); + + running = s->running_context; + + memset(routine, 0, sizeof(_routine_t)); + routine->name = n; + + if(n && f) + routine->type = _IT_NATIVE; + else if(n && !f) + routine->type = _IT_SCRIPT; +#ifdef MB_ENABLE_LAMBDA + else if(!n && !f) + routine->type = _IT_LAMBDA; +#endif /* MB_ENABLE_LAMBDA */ + + switch(routine->type) { + case _IT_SCRIPT: + routine->func.basic.scope = _create_running_context(true); + + break; +#ifdef MB_ENABLE_LAMBDA + case _IT_LAMBDA: + _create_ref(&routine->func.lambda.ref, _unref_routine, _DT_ROUTINE, s); + _ref(&routine->func.lambda.ref, routine); + + break; +#endif /* MB_ENABLE_LAMBDA */ + case _IT_NATIVE: + routine->func.native.entry = f; + + break; + } + +#ifdef MB_ENABLE_SOURCE_TRACE + if(s->source_file) + routine->source_file = mb_strdup(s->source_file, 0); +#endif /* MB_ENABLE_SOURCE_TRACE */ +} + +/* Begin parsing a routine */ +static int _begin_routine(mb_interpreter_t* s) { + int result = MB_FUNC_OK; + _parsing_context_t* context = 0; + unsigned short before = 0; + + mb_assert(s); + + context = s->parsing_context; + before = context->routine_state++; + if(before > context->routine_state) { + context->routine_state--; + result = MB_FUNC_ERR; + _handle_error_now(s, SE_RN_TOO_MANY_ROUTINES, s->last_error_file, result); + } + + return result; +} + +/* End parsing a routine */ +static bool_t _end_routine(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + if(!context->routine_state) { + _handle_error_now(s, SE_RN_INVALID_ROUTINE, s->source_file, MB_FUNC_ERR); + + return false; + } + context->routine_state--; + + return true; +} + +/* Begin parsing the parameter list of a routine */ +static void _begin_routine_parameter_list(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + context->routine_params_state++; +} + +/* End parsing the parameter list of a routine */ +static void _end_routine_parameter_list(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + context->routine_params_state--; +} + +/* Duplicate a parameter from a parameter list to variable dictionary */ +static _object_t* _duplicate_parameter(void* data, void* extra, _running_context_t* running) { + _var_t* ref = 0; + _var_t* var = 0; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(running); + + if(data == 0) + return 0; + + ref = (_var_t*)data; + + var = _create_var(&obj, ref->name, 0, true); + + _ht_set_or_insert(running->var_dict, var->name, obj); + + return obj; +} + +/* Clone a routine */ +static _routine_t* _clone_routine(_routine_t* sub, void* c, bool_t toupval) { + _routine_t* result = 0; +#ifdef MB_ENABLE_CLASS + _class_t* instance = (_class_t*)c; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(c); +#endif /* MB_ENABLE_CLASS */ + + mb_assert(sub); + +#ifdef MB_ENABLE_LAMBDA + if(toupval || sub->type == _IT_LAMBDA) + result = sub; +#else /* MB_ENABLE_LAMBDA */ + mb_unrefvar(toupval); +#endif /* MB_ENABLE_LAMBDA */ + + if(!result) { + result = (_routine_t*)mb_malloc(sizeof(_routine_t)); + memset(result, 0, sizeof(_routine_t)); + result->name = sub->name; +#ifdef MB_ENABLE_SOURCE_TRACE + result->source_file = sub->source_file; +#endif /* MB_ENABLE_SOURCE_TRACE */ +#ifdef MB_ENABLE_CLASS + result->instance = instance; +#endif /* MB_ENABLE_CLASS */ + result->is_cloned = true; + result->type = sub->type; + result->func = sub->func; + } + + return result; +} + +#ifdef MB_ENABLE_LAMBDA +/* Initialize a lambda */ +static _running_context_t* _init_lambda(mb_interpreter_t* s, _routine_t* routine) { + _running_context_t* result = 0; + _lambda_t* lambda = 0; + + mb_assert(s && routine); + + _init_routine(s, routine, 0, 0); + mb_assert(routine->type == _IT_LAMBDA); + lambda = &routine->func.lambda; + lambda->scope = _create_running_context(true); + result = _push_scope_by_routine(s, lambda->scope); + + return result; +} + +/* Unreference a lambda routine */ +static void _unref_routine(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_routine(ref->s, (_routine_t*)data); +} + +/* Destroy a lambda routine */ +static void _destroy_routine(mb_interpreter_t* s, _routine_t* r) { + _gc_t* gc = 0; + + mb_assert(r); + + if(s) gc = &s->gc; + if(!r->is_cloned) { + if(r->name) { + safe_free(r->name); + } +#ifdef MB_ENABLE_SOURCE_TRACE + if(r->source_file) { + safe_free(r->source_file); + } +#endif /* MB_ENABLE_SOURCE_TRACE */ + switch(r->type) { + case _IT_SCRIPT: + if(r->func.basic.scope) { + _destroy_scope(s, r->func.basic.scope); + r->func.basic.scope = 0; + } + if(r->func.basic.parameters) + _ls_destroy(r->func.basic.parameters); + + break; + case _IT_LAMBDA: + _destroy_ref(&r->func.lambda.ref); + if(r->func.lambda.scope->var_dict) { + _ht_foreach(r->func.lambda.scope->var_dict, _destroy_object); + _ht_destroy(r->func.lambda.scope->var_dict); + } + safe_free(r->func.lambda.scope); + if(r->func.lambda.parameters) + _ls_destroy(r->func.lambda.parameters); + if(r->func.lambda.outer_scope && !_ht_find(gc->collected_table, &r->func.lambda.outer_scope->ref)) + _unref(&r->func.lambda.outer_scope->ref, r->func.lambda.outer_scope); + if(r->func.lambda.upvalues) + _ht_destroy(r->func.lambda.upvalues); + + break; + case _IT_NATIVE: /* Do nothing */ + break; + } + } + safe_free(r); +} + +/* Mark an upvalue of a lambda */ +static void _mark_upvalue(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj, const char* n) { + _running_context_t* running = 0; + _running_context_t* found_in_scope = 0; + _ls_node_t* scp = 0; + + mb_assert(s && lambda && obj); + + running = s->running_context; + scp = _search_identifier_in_scope_chain(s, running, n, _PATHING_NORMAL, 0, &found_in_scope); + if(scp && found_in_scope) { + if(!found_in_scope->refered_lambdas) + found_in_scope->refered_lambdas = _ls_create(); + if(!_ls_find(found_in_scope->refered_lambdas, lambda, (_ls_compare)_ht_cmp_intptr, 0)) + _ls_pushback(found_in_scope->refered_lambdas, lambda); + } + + if(!lambda->upvalues) + lambda->upvalues = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); + _ht_set_or_insert(lambda->upvalues, obj->data.variable->name, obj); +} + +/* Try to mark upvalues of a lambda */ +static void _try_mark_upvalue(mb_interpreter_t* s, _routine_t* r, _object_t* obj) { + _lambda_t* lambda = 0; + _ls_node_t* node = 0; + _object_t* inner = 0; + + mb_assert(s && r && obj); + mb_assert(r->type == _IT_LAMBDA); + + lambda = &r->func.lambda; + + switch(obj->type) { + case _DT_VAR: + node = _ht_find(lambda->scope->var_dict, obj->data.variable->name); + if(node && node->data) { + /* Use variables in the inner scope */ + inner = (_object_t*)node->data; + obj->type = _DT_VAR; + obj->data = inner->data; + obj->ref = true; + } else { + /* Mark upvalues referencing outer scope chain */ + _mark_upvalue(s, lambda, obj, obj->data.variable->name); + } + + break; + default: /* Do nothing */ + break; + } +} + +/* Create an outer scope, which is a referenced type */ +static _running_context_ref_t* _create_outer_scope(mb_interpreter_t* s) { + _running_context_ref_t* result = 0; + + mb_assert(s); + + result = (_running_context_ref_t*)mb_malloc(sizeof(_running_context_ref_t)); + memset(result, 0, sizeof(_running_context_ref_t)); + _create_ref(&result->ref, _unref_outer_scope, _DT_OUTER_SCOPE, s); + result->scope = _create_running_context(true); + + return result; +} + +/* Unreference an outer scope */ +static void _unref_outer_scope(_ref_t* ref, void* data) { + mb_assert(ref); + + if(*ref->count == _NONE_REF) + _destroy_outer_scope((_running_context_ref_t*)data); +} + +/* Destroy an outer scope */ +static void _destroy_outer_scope(_running_context_ref_t* p) { + mb_assert(p); + + if(p) { + _running_context_ref_t* scope = p; + p = p->prev; + _destroy_scope(scope->ref.s, scope->scope); + _destroy_ref(&scope->ref); + safe_free(scope); + } + while(p) { + _running_context_ref_t* scope = p; + p = p->prev; + _unref(&scope->ref, scope); + } +} + +/* Do nothing, this is a helper function for lambda */ +static int _do_nothing_on_ht_for_lambda(void* data, void* extra) { + int result = _OP_RESULT_NORMAL; + mb_unrefvar(data); + mb_unrefvar(extra); + + return result; +} + +/* Fill an outer scope with the original value */ +static int _fill_with_upvalue(void* data, void* extra, _upvalue_scope_tuple_t* tuple) { + _object_t* obj = (_object_t*)data; + const char* n = (const char*)extra; + unsigned ul = 0; + _ls_node_t* ast = 0; + _ls_node_t* nput = 0; + + nput = _ht_find(tuple->outer_scope->scope->var_dict, (void*)n); + if(!nput) { + _ls_node_t* nori = 0; +#ifdef MB_ENABLE_CLASS + if(tuple->instance) + nori = _search_identifier_in_scope_chain(tuple->s, tuple->scope, n, _PATHING_NORMAL, 0, 0); + else + nori = _ht_find(tuple->scope->var_dict, (void*)n); +#else /* MB_ENABLE_CLASS */ + nori = _ht_find(tuple->scope->var_dict, (void*)n); +#endif /* MB_ENABLE_CLASS */ + if(nori) { + _object_t* ovar = 0; + _var_t* var = _create_var(&ovar, n, 0, true); + obj = (_object_t*)nori->data; + _clone_object(tuple->s, obj, var->data, true, true); + _REF(var->data) + if(_IS_ROUTINE(obj) && obj->data.routine->type != _IT_LAMBDA) { + ovar->ref = true; + var->data->ref = true; + } +#ifdef MB_ENABLE_CLASS + if(obj->type == _DT_VAR) + var->pathing = obj->data.variable->pathing; + else + var->pathing = false; +#endif /* MB_ENABLE_CLASS */ + ul = _ht_set_or_insert(tuple->outer_scope->scope->var_dict, ovar->data.variable->name, ovar); + mb_assert(ul); + _ht_set_or_insert(tuple->filled, extra, data); + + ast = tuple->lambda->entry; + while(ast && ast != tuple->lambda->end->next) { + _object_t* aobj = (_object_t*)ast->data; + switch(aobj->type) { + case _DT_VAR: + if(!strcmp(aobj->data.variable->name, ovar->data.variable->name)) { + aobj->type = _DT_VAR; + aobj->data = ovar->data; + aobj->ref = true; + } + + break; + default: /* Do nothing */ + break; + } + ast = ast->next; + } + } + } + + return 0; +} + +/* Remove filled upvalues */ +static int _remove_filled_upvalue(void* data, void* extra, _ht_node_t* ht) { + _ht_remove_exist(data, extra, ht); + + return _OP_RESULT_NORMAL; +} + +/* Fill an outer scope with the original one */ +static int _fill_outer_scope(void* data, void* extra, _upvalue_scope_tuple_t* tuple) { + _lambda_t* lambda = (_lambda_t*)data; + mb_unrefvar(extra); + + if(lambda->upvalues) { + tuple->filled = _ht_create(0, _ht_cmp_intptr, _ht_hash_intptr, 0); { + tuple->lambda = lambda; + _HT_FOREACH(lambda->upvalues, _do_nothing_on_ht_for_lambda, _fill_with_upvalue, tuple); + tuple->lambda = 0; + } + _HT_FOREACH(tuple->filled, _do_nothing_on_ht_for_lambda, _remove_filled_upvalue, lambda->upvalues); + if(!_ht_count(lambda->upvalues)) { + _ht_destroy(lambda->upvalues); + lambda->upvalues = 0; + } + _ht_destroy(tuple->filled); + } + + if(lambda->outer_scope) { + _running_context_ref_t* root_ref = _get_root_ref_scope(lambda->outer_scope); + root_ref->prev = tuple->outer_scope; + root_ref->scope->prev = tuple->outer_scope->scope; + } else { + lambda->outer_scope = tuple->outer_scope; + } + + _ref(&tuple->outer_scope->ref, tuple->outer_scope); + + return 0; +} + +/* Link the local scope of a lambda and all upvalue scopes in chain to a given scope */ +static _running_context_t* _link_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) { + _running_context_ref_t* root_ref = 0; + _running_context_t* root = 0; + + if(lambda->outer_scope) { + lambda->scope->prev = lambda->outer_scope->scope; + root_ref = _get_root_ref_scope(lambda->outer_scope); + root_ref->scope->prev = 0; + } + root = _get_root_scope(lambda->scope); + + if(weak) { + _push_weak_scope_by_routine(s, root, 0); + } else { + root->prev = s->running_context; + s->running_context = lambda->scope; + } + + return lambda->scope; +} + +/* Unlink the local scope of a lambda and all upvalue scopes in chain from a given scope */ +static _running_context_t* _unlink_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) { + _running_context_ref_t* root_ref = 0; + _running_context_t* root = 0; + + if(lambda->outer_scope) { + root_ref = _get_root_ref_scope(lambda->outer_scope); + root = root_ref->scope; + } else { + root = lambda->scope; + } + + if(weak) + _pop_weak_scope(s, root); + else + s->running_context = root->prev; + + root->prev = 0; + lambda->scope->prev = 0; + + return lambda->scope; +} + +/* Check whether an object is a valid lambda body node */ +static bool_t _is_valid_lambda_body_node(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj) { + mb_unrefvar(s); + mb_unrefvar(lambda); + + return ( + !_IS_FUNC(obj, _core_def) && + !_IS_FUNC(obj, _core_enddef) && +#ifdef MB_ENABLE_CLASS + !_IS_FUNC(obj, _core_class) && + !_IS_FUNC(obj, _core_endclass) && +#endif /* MB_ENABLE_CLASS */ + true + ); +} +#endif /* MB_ENABLE_LAMBDA */ + +#ifdef MB_ENABLE_CLASS +/* Create a scope reference to an exist one by a class */ +static _running_context_t* _reference_scope_by_class(mb_interpreter_t* s, _running_context_t* p, _class_t* c) { + _running_context_t* result = 0; + mb_unrefvar(c); + + mb_assert(s && p); + + if(p->meta == _SCOPE_META_REF) + p = p->ref; + + result = _create_running_context(false); + result->meta = _SCOPE_META_REF; + result->ref = p; + + return result; +} + +/* Push a scope by a class */ +static _running_context_t* _push_scope_by_class(mb_interpreter_t* s, _running_context_t* p) { + mb_assert(s); + + if(_find_scope(s, p)) + p = _reference_scope_by_class(s, p, 0); + p->prev = s->running_context; + s->running_context = p; + + return s->running_context; +} + +/* Try to search an identifire from a class */ +static _ls_node_t* _search_identifier_in_class(mb_interpreter_t* s, _class_t* instance, const char* n, _ht_node_t** ht, _running_context_t** sp) { + _ls_node_t* result = 0; + _ls_node_t* node = 0; + _class_t* meta = 0; + + mb_assert(s && instance && n); + + result = _ht_find(instance->scope->var_dict, (void*)n); + if(result) { + if(ht) *ht = instance->scope->var_dict; + if(sp) *sp = instance->scope; + } + + if(!result) { + node = instance->meta_list ? instance->meta_list->next : 0; + while(node) { + meta = (_class_t*)node->data; + result = _search_identifier_in_class(s, meta, n, ht, sp); + if(result) + break; + node = node->next; + } + } + + return result; +} +#endif /* MB_ENABLE_CLASS */ + +/* Create a scope reference to an exist one by a routine */ +static _running_context_t* _reference_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) { + _running_context_t* result = 0; + + mb_assert(s && p); + + if(p->meta == _SCOPE_META_REF) + p = p->ref; + + result = _create_running_context(false); + result->meta = _SCOPE_META_REF; + result->ref = p; + if(r && r->func.basic.parameters) { + result->var_dict = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); + _LS_FOREACH(r->func.basic.parameters, _do_nothing_on_object, _duplicate_parameter, result); + } + + return result; +} + +/* Push a weak scope by a routine */ +static _running_context_t* _push_weak_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) { + mb_assert(s); + + if(_find_scope(s, p)) + p = _reference_scope_by_routine(s, p, r); + if(p) + p->prev = s->running_context; + + return p; +} + +/* Push a scope by a routine */ +static _running_context_t* _push_scope_by_routine(mb_interpreter_t* s, _running_context_t* p) { + mb_assert(s); + + if(_find_scope(s, p)) + p = _reference_scope_by_routine(s, p, 0); + if(p) { + p->prev = s->running_context; + s->running_context = p; + } + + return s->running_context; +} + +/* Destroy a scope */ +static void _destroy_scope(mb_interpreter_t* s, _running_context_t* p) { + mb_unrefvar(s); + + if(p->var_dict) { + _ht_foreach(p->var_dict, _destroy_object); + _ht_destroy(p->var_dict); +#ifdef MB_ENABLE_LAMBDA + if(p->refered_lambdas) { + _ls_destroy(p->refered_lambdas); + p->refered_lambdas = 0; + } +#endif /* MB_ENABLE_LAMBDA */ + } + safe_free(p); +} + +/* Pop a weak scope */ +static _running_context_t* _pop_weak_scope(mb_interpreter_t* s, _running_context_t* p) { + mb_assert(s); + + if(p) + p->prev = 0; + + return p; +} + +/* Pop a scope */ +static _running_context_t* _pop_scope(mb_interpreter_t* s, bool_t tidy) { + _running_context_t* running = 0; + + mb_assert(s); + + running = s->running_context; + s->running_context = running->prev; + running->prev = 0; + if(running->meta == _SCOPE_META_REF) + _destroy_scope(s, running); + else if(tidy) + _out_of_scope(s, running, 0, 0, true); + + return s->running_context; +} + +/* Out of current scope */ +static void _out_of_scope(mb_interpreter_t* s, _running_context_t* running, void* instance, _routine_t* routine, bool_t lose) { +#ifdef MB_ENABLE_LAMBDA + _upvalue_scope_tuple_t tuple; +#endif /* MB_ENABLE_LAMBDA */ + mb_unrefvar(routine); + + mb_assert(s); + +#ifdef MB_ENABLE_LAMBDA + if(running->refered_lambdas) { + tuple.s = s; +# ifdef MB_ENABLE_CLASS + tuple.instance = (_class_t*)instance; +# else /* MB_ENABLE_CLASS */ + mb_unrefvar(instance); +# endif /* MB_ENABLE_CLASS */ + tuple.scope = running; + tuple.outer_scope = _create_outer_scope(s); + tuple.lambda = 0; + + _LS_FOREACH(running->refered_lambdas, _do_nothing_on_ht_for_lambda, _fill_outer_scope, &tuple); + + _ls_destroy(running->refered_lambdas); + running->refered_lambdas = 0; + } +#else /* MB_ENABLE_LAMBDA */ + mb_unrefvar(instance); +#endif /* MB_ENABLE_LAMBDA */ + + if(lose) { + if(running->var_dict) + _ht_foreach(running->var_dict, _lose_object); + } +} + +/* Find a scope in a scope chain */ +static _running_context_t* _find_scope(mb_interpreter_t* s, _running_context_t* p) { + _running_context_t* running = 0; + + mb_assert(s); + + running = s->running_context; + while(running) { + if(running == p) + return running; + + if(running->ref == p) + return running->ref; + + running = running->prev; + } + + return running; +} + +/* Get the root scope in a scope chain */ +static _running_context_t* _get_root_scope(_running_context_t* scope) { + _running_context_t* result = 0; + + while(scope) { + result = scope; + scope = scope->prev; + } + + return result; +} + +#ifdef MB_ENABLE_LAMBDA +/* Get the root referenced scope in a referenced scope chain */ +static _running_context_ref_t* _get_root_ref_scope(_running_context_ref_t* scope) { + _running_context_ref_t* result = 0; + + while(scope) { + result = scope; + scope = scope->prev; + } + + return result; +} +#endif /* MB_ENABLE_LAMBDA */ + +/* Get a proper scope to define a routine */ +static _running_context_t* _get_scope_to_add_routine(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + _running_context_t* running = 0; + unsigned short class_state = _CLASS_STATE_NONE; + + mb_assert(s); + + context = s->parsing_context; + running = s->running_context; +#ifdef MB_ENABLE_CLASS + class_state = context->class_state; +#endif /* MB_ENABLE_CLASS */ + if(class_state != _CLASS_STATE_NONE) { + if(running) + running = running->prev; + } else { + while(running) { + if(running->meta == _SCOPE_META_ROOT) + break; + + running = running->prev; + } + } + + return running; +} + +/* Try to search an identifier in a scope chain */ +static _ls_node_t* _search_identifier_in_scope_chain(mb_interpreter_t* s, _running_context_t* scope, const char* n, int pathing, _ht_node_t** ht, _running_context_t** sp) { + _ls_node_t* result = 0; + _running_context_t* running = 0; + _ht_node_t* fn = 0; + _running_context_t* fs = 0; + + mb_assert(s && n); + +#ifdef MB_ENABLE_CLASS + if(pathing) { + result = _search_identifier_accessor(s, scope, n, &fn, &fs, pathing == _PATHING_UNKNOWN_FOR_NOT_FOUND); + if(result) + goto _exit; + } + + if(s->last_routine && s->last_routine->instance) { + _class_t* lastinst = s->last_routine->instance; + s->last_routine->instance = 0; + result = _search_identifier_in_class(s, lastinst, n, &fn, &fs); + s->last_routine->instance = lastinst; + if(result) + goto _exit; + } +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(pathing); +#endif /* MB_ENABLE_CLASS */ + + if(scope) + running = scope; + else + running = s->running_context; + while(running && !result) { + if(running->var_dict) { + result = _ht_find(running->var_dict, (void*)n); + fn = running->var_dict; + fs = running; + if(!result && running->meta == _SCOPE_META_REF) { + result = _ht_find(running->ref->var_dict, (void*)n); + fn = running->ref->var_dict; + fs = running->ref; + } + if(result) + break; + } + + running = running->prev; + } + +#ifdef MB_ENABLE_CLASS +_exit: +#endif /* MB_ENABLE_CLASS */ + if(ht) *ht = fn; + if(sp) *sp = fs; + + return result; +} + +/* Try to search an array in a scope chain */ +static _array_t* _search_array_in_scope_chain(mb_interpreter_t* s, _array_t* i, _object_t** o) { + _object_t* obj = 0; + _ls_node_t* scp = 0; + _array_t* result = 0; + + mb_assert(s && i); + + result = i; + scp = _search_identifier_in_scope_chain(s, 0, result->name, _PATHING_NONE, 0, 0); + if(scp) { + obj = (_object_t*)scp->data; + if(obj && obj->type == _DT_ARRAY) { + result = obj->data.array; + if(o) *o = obj; + } + } + + return result; +} + +/* Try to search a variable in a scope chain */ +static _var_t* _search_var_in_scope_chain(mb_interpreter_t* s, _var_t* i) { + _object_t* obj = 0; + _ls_node_t* scp = 0; + _var_t* result = 0; + + mb_assert(s && i); + + result = i; + scp = _search_identifier_in_scope_chain(s, 0, result->name, _PATHING_NORMAL, 0, 0); + if(scp) { + obj = (_object_t*)scp->data; + if(obj && obj->type == _DT_VAR) + result = obj->data.variable; + } + + return result; +} + +/* Try to search an identifier accessor in a scope */ +static _ls_node_t* _search_identifier_accessor(mb_interpreter_t* s, _running_context_t* scope, const char* n, _ht_node_t** ht, _running_context_t** sp, bool_t unknown_for_not_found) { + _ls_node_t* result = 0; + _object_t* obj = 0; + char acc[_SINGLE_SYMBOL_MAX_LENGTH]; + int i = 0; + int j = 0; +#ifdef MB_ENABLE_CLASS + _class_t* instance = 0; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(unknown_for_not_found); +#endif /* MB_ENABLE_CLASS */ + + mb_assert(s && n); + + while((i == 0) || (i > 0 && n[i - 1])) { + acc[j] = n[i]; + if(_is_accessor_char(acc[j]) || acc[j] == _ZERO_CHAR) { + acc[j] = _ZERO_CHAR; + do { +#ifdef MB_ENABLE_CLASS + if(instance) { + result = _search_identifier_in_class(s, instance, acc, ht, sp); + if(!result && unknown_for_not_found) { + result = (_ls_node_t*)&_LS_NODE_UNKNOWN; + + return result; + } + + break; + } +#endif /* MB_ENABLE_CLASS */ + + result = _search_identifier_in_scope_chain(s, scope, acc, _PATHING_NONE, ht, sp); + } while(0); + + if(!result) + return 0; + obj = (_object_t*)result->data; + if(!obj) + return 0; + switch(obj->type) { + case _DT_VAR: +#ifdef MB_ENABLE_CLASS + if(obj->data.variable->data->type == _DT_CLASS) + instance = obj->data.variable->data->data.instance; +#endif /* MB_ENABLE_CLASS */ + + break; +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + instance = obj->data.instance; + + break; +#endif /* MB_ENABLE_CLASS */ + case _DT_ARRAY: /* Fall through */ + case _DT_ROUTINE: /* Do nothing */ + break; + default: + mb_assert(0 && "Unsupported."); + + return 0; + } + + j = 0; + i++; + + continue; + } + j++; + i++; + } + + return result; +} + +/* Create a variable object */ +static _var_t* _create_var(_object_t** oobj, const char* n, size_t ns, bool_t dup_name) { + _object_t* obj = 0; + _var_t* var = 0; + + var = (_var_t*)mb_malloc(sizeof(_var_t)); + memset(var, 0, sizeof(_var_t)); + if(dup_name) + var->name = mb_strdup(n, ns); + else + var->name = (char*)n; + var->data = _create_object(); + + if(!oobj || !(*oobj)) + obj = _create_object(); + else + obj = *oobj; + _MAKE_NIL(obj); + obj->type = _DT_VAR; + obj->data.variable = var; + obj->ref = false; + + if(oobj) *oobj = obj; + + return var; +} + +/* Create an _object_t struct */ +static _object_t* _create_object(void) { + _object_t* result = 0; + + result = (_object_t*)mb_malloc(sizeof(_object_t)); + _MAKE_NIL(result); + + return result; +} + +/* Clone the data of an object */ +static int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt, bool_t toupval, bool_t deep) { + int result = 0; + + mb_assert(obj && tgt); + + _MAKE_NIL(tgt); + tgt->type = _DT_NIL; + if(_is_internal_object(obj)) + goto _exit; + tgt->type = obj->type; + switch(obj->type) { + case _DT_VAR: + _clone_object(s, obj->data.variable->data, tgt, toupval, deep); + + break; + case _DT_STRING: + tgt->data.string = mb_strdup(obj->data.string, 0); + + break; +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + tgt->data.usertype_ref = _create_usertype_ref( + obj->data.usertype_ref->ref.s, + obj->data.usertype_ref->clone(obj->data.usertype_ref->ref.s, obj->data.usertype_ref->usertype), + obj->data.usertype_ref->dtor, obj->data.usertype_ref->clone, + obj->data.usertype_ref->hash, obj->data.usertype_ref->cmp, obj->data.usertype_ref->fmt + ); + if(obj->data.usertype_ref->calc_operators) { + tgt->data.usertype_ref->calc_operators = (_calculation_operator_info_t*)mb_malloc(sizeof(_calculation_operator_info_t)); + memcpy(tgt->data.usertype_ref->calc_operators, obj->data.usertype_ref->calc_operators, sizeof(_calculation_operator_info_t)); + } + tgt->data.usertype_ref->coll_func = obj->data.usertype_ref->coll_func; + tgt->data.usertype_ref->generic_func = obj->data.usertype_ref->generic_func; + _ref(&tgt->data.usertype_ref->ref, tgt->data.usertype_ref); + + break; +#endif /* MB_ENABLE_USERTYPE_REF */ + case _DT_FUNC: + tgt->data.func->name = mb_strdup(obj->data.func->name, strlen(obj->data.func->name) + 1); + tgt->data.func->pointer = obj->data.func->pointer; + + break; + case _DT_ARRAY: + if(deep) { + tgt->data.array = _clone_array(s, obj->data.array); + } else { + tgt->data.array = obj->data.array; +#ifdef MB_ENABLE_ARRAY_REF + _ref(&obj->data.array->ref, obj->data.array); +#endif /* MB_ENABLE_ARRAY_REF */ + } + + break; +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: + if(deep) { + tgt->data.list = _create_list(obj->data.list->ref.s); + _ref(&tgt->data.list->ref, tgt->data.list); + _LS_FOREACH(obj->data.list->list, _do_nothing_on_object, _clone_to_list, tgt->data.list); + } else { + tgt->data.list = obj->data.list; + _ref(&obj->data.list->ref, obj->data.list); + } + + break; + case _DT_LIST_IT: + tgt->data.list_it = _create_list_it(obj->data.list_it->list, true); + + break; + case _DT_DICT: + if(deep) { + tgt->data.dict = _create_dict(obj->data.dict->ref.s); + _ref(&tgt->data.dict->ref, tgt->data.dict); + _HT_FOREACH(obj->data.dict->dict, _do_nothing_on_object, _clone_to_dict, tgt->data.dict); + } else { + tgt->data.dict = obj->data.dict; + _ref(&obj->data.dict->ref, obj->data.dict); + } + + break; + case _DT_DICT_IT: + tgt->data.dict_it = _create_dict_it(obj->data.dict_it->dict, true); + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ + case _DT_LABEL: + tgt->data.label->name = mb_strdup(obj->data.label->name, 0); + tgt->data.label->node = obj->data.label->node; + + break; +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + if(deep) { + tgt->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); + _init_class(s, tgt->data.instance, mb_strdup(obj->data.instance->name, 0)); + tgt->data.instance->created_from = obj->data.instance->created_from; + _push_scope_by_class(s, tgt->data.instance->scope); + _traverse_class(obj->data.instance, _clone_clsss_field, _clone_class_meta_link, _META_LIST_MAX_DEPTH, false, tgt->data.instance, 0); + if(_search_class_hash_and_compare_functions(s, tgt->data.instance) != MB_FUNC_OK) { + mb_assert(0 && "Impossible."); + } + tgt->data.instance->userdata = obj->data.instance->userdata; + _pop_scope(s, false); + } else { + tgt->data.instance = obj->data.instance; + _ref(&obj->data.instance->ref, obj->data.instance); + } + + break; +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + tgt->data.routine = _clone_routine( + obj->data.routine, +#ifdef MB_ENABLE_CLASS + obj->data.routine->instance, +#else /* MB_ENABLE_CLASS */ + 0, +#endif /* MB_ENABLE_CLASS */ + toupval + ); + + break; + case _DT_NIL: /* Fall through */ + case _DT_UNKNOWN: /* Fall through */ + case _DT_INT: /* Fall through */ + case _DT_REAL: /* Fall through */ + case _DT_TYPE: /* Fall through */ + case _DT_SEP: /* Fall through */ + case _DT_EOS: /* Fall through */ + case _DT_USERTYPE: + tgt->data = obj->data; + + break; + default: + mb_assert(0 && "Invalid type."); + + break; + } + tgt->ref = false; +#ifdef MB_ENABLE_SOURCE_TRACE + tgt->source_pos = 0; + tgt->source_row = 0; + tgt->source_col = 0; +#else /* MB_ENABLE_SOURCE_TRACE */ + tgt->source_pos = 0; +#endif /* MB_ENABLE_SOURCE_TRACE */ + ++result; + +_exit: + return result; +} + +/* Dispose the data of an object */ +static int _dispose_object(_object_t* obj) { + int result = 0; + _var_t* var = 0; + + mb_assert(obj); + + if(_is_internal_object(obj)) + goto _exit; + switch(obj->type) { + case _DT_VAR: + if(!obj->ref) { + var = (_var_t*)obj->data.variable; + safe_free(var->name); + mb_assert(var->data->type != _DT_VAR); + if(_IS_ME(var)) + _destroy_object_capsule_only(var->data, 0); + else + _destroy_object(var->data, 0); + safe_free(var); + } + + break; + case _DT_STRING: + if(!obj->ref) { + if(obj->data.string) { + safe_free(obj->data.string); + } + } + + break; + case _DT_FUNC: + safe_free(obj->data.func->name); + safe_free(obj->data.func); + + break; + _UNREF_USERTYPE_REF(obj) + _UNREF_ARRAY(obj) + _UNREF_COLL(obj) + _UNREF_CLASS(obj) + _UNREF_COLL_IT(obj) + _UNREF_ROUTINE(obj) + case _DT_LABEL: + if(!obj->ref) { + safe_free(obj->data.label->name); + safe_free(obj->data.label); + } + + break; +#ifdef MB_ENABLE_SOURCE_TRACE + case _DT_PREV_IMPORT: /* Fall through */ + case _DT_POST_IMPORT: + if(!obj->ref) { + if(obj->data.import_info) { + if(obj->data.import_info->source_file) { + safe_free(obj->data.import_info->source_file); + } + safe_free(obj->data.import_info); + } + } + + break; +#endif /* MB_ENABLE_SOURCE_TRACE */ + case _DT_NIL: /* Fall through */ + case _DT_UNKNOWN: /* Fall through */ + case _DT_INT: /* Fall through */ + case _DT_REAL: /* Fall through */ + case _DT_TYPE: /* Fall through */ + case _DT_SEP: /* Fall through */ + case _DT_EOS: /* Fall through */ + case _DT_USERTYPE: /* Do nothing */ + break; + default: + mb_assert(0 && "Invalid type."); + + break; + } + obj->ref = false; + obj->type = _DT_NIL; + memset(&obj->data, 0, sizeof(obj->data)); +#ifdef MB_ENABLE_SOURCE_TRACE + obj->source_pos = 0; + obj->source_row = 0; + obj->source_col = 0; +#else /* MB_ENABLE_SOURCE_TRACE */ + obj->source_pos = 0; +#endif /* MB_ENABLE_SOURCE_TRACE */ + ++result; + +_exit: + return result; +} + +/* Destroy an object and its data */ +static int _destroy_object(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + if(!_dispose_object(obj)) + goto _exit; + safe_free(obj); + +_exit: + return result; +} + +/* Destroy an object, including its data and extra data */ +static int _destroy_object_with_extra(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + + mb_assert(data); + + obj = (_object_t*)data; + if(!_dispose_object(obj)) + goto _exit; + safe_free(obj); + obj = (_object_t*)extra; + if(!_dispose_object(obj)) + goto _exit; + safe_free(obj); + +_exit: + return result; +} + +/* Destroy an object which is not come from compile time */ +static int _destroy_object_not_compile_time(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + if(!obj->source_pos) { + if(!_dispose_object(obj)) + goto _exit; + safe_free(obj); + } + +_exit: + return result; +} + +/* Destroy only the capsule (wrapper) of an object, leave the data behind */ +static int _destroy_object_capsule_only(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _object_t* obj = 0; + mb_unrefvar(extra); + + mb_assert(data); + + obj = (_object_t*)data; + safe_free(obj); + + return result; +} + +/* Do nothing with an object, this is a helper function */ +static int _do_nothing_on_object(void* data, void* extra) { + int result = _OP_RESULT_NORMAL; + mb_unrefvar(data); + mb_unrefvar(extra); + + return result; +} + +/* Lose an object of of scope */ +static int _lose_object(void* data, void* extra) { + int result = _OP_RESULT_NORMAL; + _object_t* obj = 0; + bool_t make_nil = true; + + mb_assert(data && extra); + + obj = (_object_t*)data; + switch(obj->type) { + case _DT_VAR: + _lose_object(obj->data.variable->data, extra); + make_nil = false; + + break; + _UNREF_USERTYPE_REF(obj) + _UNREF_ARRAY(obj) + _UNREF_COLL(obj) + _UNREF_CLASS(obj) + _UNREF_ROUTINE(obj) + default: + make_nil = false; + + break; + } + + if(make_nil) { + _MAKE_NIL(obj); + } + + return result; +} + +/* Remove an object referenced to source code */ +static int _remove_source_object(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + mb_unrefvar(extra); + + mb_assert(data); + + return result; +} + +/* Destroy a chunk of memory */ +static int _destroy_memory(void* data, void* extra) { + int result = _OP_RESULT_NORMAL; + mb_unrefvar(extra); + + mb_assert(data); + + safe_free(data); + + return result; +} + +/* Compare two numbers from two objects */ +static int _compare_numbers(const _object_t* first, const _object_t* second) { + int result = 0; + + mb_assert(first && second); + mb_assert((first->type == _DT_INT || first->type == _DT_REAL) && (second->type == _DT_INT || second->type == _DT_REAL)); + + if(first->type == _DT_INT && second->type == _DT_INT) { + if(first->data.integer > second->data.integer) + result = 1; + else if(first->data.integer < second->data.integer) + result = -1; + } else if(first->type == _DT_REAL && second->type == _DT_REAL) { + if(first->data.float_point > second->data.float_point) + result = 1; + else if(first->data.float_point < second->data.float_point) + result = -1; + } else { + if((first->type == _DT_INT ? (real_t)first->data.integer : first->data.float_point) > (second->type == _DT_INT ? (real_t)second->data.integer : second->data.float_point)) + result = 1; + else if((first->type == _DT_INT ? (real_t)first->data.integer : first->data.float_point) > (second->type == _DT_INT ? (real_t)second->data.integer : second->data.float_point)) + result = -1; + } + + return result; +} + +/* Determine whether an object is internal */ +static bool_t _is_internal_object(_object_t* obj) { + bool_t result = false; + + mb_assert(obj); + + result = ( + (_exp_assign == obj) || + (_OBJ_BOOL_TRUE == obj) || (_OBJ_BOOL_FALSE == obj) + ); + + return result; +} + +/* Convert a public mb_data_e type to an internal _data_e */ +static _data_e _public_type_to_internal_type(mb_data_e t) { + switch(t) { + case MB_DT_NIL: + return _DT_NIL; + case MB_DT_INT: + return _DT_INT; + case MB_DT_REAL: + return _DT_REAL; + case MB_DT_STRING: + return _DT_STRING; + case MB_DT_TYPE: + return _DT_TYPE; + case MB_DT_USERTYPE: + return _DT_USERTYPE; +#ifdef MB_ENABLE_USERTYPE_REF + case MB_DT_USERTYPE_REF: + return _DT_USERTYPE_REF; +#endif /* MB_ENABLE_USERTYPE_REF */ + case MB_DT_ARRAY: + return _DT_ARRAY; +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + return _DT_LIST; + case MB_DT_LIST_IT: + return _DT_LIST_IT; + case MB_DT_DICT: + return _DT_DICT; + case MB_DT_DICT_IT: + return _DT_DICT_IT; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + return _DT_CLASS; +#endif /* MB_ENABLE_CLASS */ + case MB_DT_ROUTINE: + return _DT_ROUTINE; + default: + return _DT_UNKNOWN; + } +} + +/* Convert an internal mb_data_e type to a public _data_e */ +static mb_data_e _internal_type_to_public_type(_data_e t) { + switch(t) { + case _DT_NIL: + return MB_DT_NIL; + case _DT_INT: + return MB_DT_INT; + case _DT_REAL: + return MB_DT_REAL; + case _DT_STRING: + return MB_DT_STRING; + case _DT_TYPE: + return MB_DT_TYPE; + case _DT_USERTYPE: + return MB_DT_USERTYPE; +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + return MB_DT_USERTYPE_REF; +#endif /* MB_ENABLE_USERTYPE_REF */ + case _DT_ARRAY: + return MB_DT_ARRAY; +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: + return MB_DT_LIST; + case _DT_LIST_IT: + return MB_DT_LIST_IT; + case _DT_DICT: + return MB_DT_DICT; + case _DT_DICT_IT: + return MB_DT_DICT_IT; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + return MB_DT_CLASS; +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + return MB_DT_ROUTINE; + default: + return MB_DT_UNKNOWN; + } +} + +/* Assign a public mb_value_t to an internal _object_t */ +static int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn) { + int result = MB_FUNC_OK; + + mb_assert(pbl && itn); + + _UNREF(itn) + + switch(pbl->type) { + case MB_DT_NIL: + itn->type = _DT_NIL; + itn->data.integer = false; + + break; + case MB_DT_UNKNOWN: + itn->type = _DT_UNKNOWN; + itn->data.integer = false; + + break; + case MB_DT_INT: + itn->type = _DT_INT; + itn->data.integer = pbl->value.integer; + + break; + case MB_DT_REAL: + itn->type = _DT_REAL; + itn->data.float_point = pbl->value.float_point; + + break; + case MB_DT_STRING: + itn->type = _DT_STRING; + itn->data.string = pbl->value.string; + itn->ref = true; + + break; + case MB_DT_TYPE: + itn->type = _DT_TYPE; + itn->data.type = pbl->value.type; + + break; + case MB_DT_USERTYPE: + itn->type = _DT_USERTYPE; + memcpy(itn->data.raw, pbl->value.bytes, sizeof(mb_val_bytes_t)); + + break; +#ifdef MB_ENABLE_USERTYPE_REF + case MB_DT_USERTYPE_REF: + itn->type = _DT_USERTYPE_REF; + itn->data.usertype_ref = (_usertype_ref_t*)pbl->value.usertype_ref; + + break; +#endif /* MB_ENABLE_USERTYPE_REF */ + case MB_DT_ARRAY: + itn->type = _DT_ARRAY; + itn->data.array = (_array_t*)pbl->value.array; + itn->ref = false; + + break; +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + itn->type = _DT_LIST; + itn->data.list = (_list_t*)pbl->value.list; + + break; + case MB_DT_LIST_IT: + itn->type = _DT_LIST_IT; + itn->data.list_it = (_list_it_t*)pbl->value.list_it; + + break; + case MB_DT_DICT: + itn->type = _DT_DICT; + itn->data.dict = (_dict_t*)pbl->value.dict; + + break; + case MB_DT_DICT_IT: + itn->type = _DT_DICT_IT; + itn->data.dict_it = (_dict_it_t*)pbl->value.dict_it; + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + itn->type = _DT_CLASS; + itn->data.instance = (_class_t*)pbl->value.instance; + + break; +#endif /* MB_ENABLE_CLASS */ + case MB_DT_ROUTINE: + itn->type = _DT_ROUTINE; + itn->data.routine = (_routine_t*)pbl->value.routine; + + break; + default: + result = MB_FUNC_ERR; + + break; + } + + return result; +} + +/* Assign an internal _object_t to a public mb_value_t */ +static int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl) { + int result = MB_FUNC_OK; + + mb_assert(pbl && itn); + + switch(itn->type) { + case _DT_VAR: + result = _internal_object_to_public_value(itn->data.variable->data, pbl); + + break; + case _DT_NIL: + mb_make_nil(*pbl); + + break; + case _DT_UNKNOWN: + pbl->type = MB_DT_UNKNOWN; + pbl->value.integer = false; + + break; + case _DT_INT: + pbl->type = MB_DT_INT; + pbl->value.integer = itn->data.integer; + + break; + case _DT_REAL: + pbl->type = MB_DT_REAL; + pbl->value.float_point = itn->data.float_point; + + break; + case _DT_STRING: + pbl->type = MB_DT_STRING; + pbl->value.string = itn->data.string; + + break; + case _DT_TYPE: + pbl->type = MB_DT_TYPE; + pbl->value.type = itn->data.type; + + break; + case _DT_USERTYPE: + pbl->type = MB_DT_USERTYPE; + memcpy(pbl->value.bytes, itn->data.raw, sizeof(mb_val_bytes_t)); + + break; +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: + pbl->type = MB_DT_USERTYPE_REF; + pbl->value.usertype_ref = itn->data.usertype_ref; + + break; +#endif /* MB_ENABLE_USERTYPE_REF */ + case _DT_ARRAY: + pbl->type = MB_DT_ARRAY; + pbl->value.array = itn->data.array; + + break; +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: + pbl->type = MB_DT_LIST; + pbl->value.list = itn->data.list; + + break; + case _DT_LIST_IT: + pbl->type = MB_DT_LIST_IT; + pbl->value.list_it = itn->data.list; + + break; + case _DT_DICT: + pbl->type = MB_DT_DICT; + pbl->value.dict = itn->data.dict; + + break; + case _DT_DICT_IT: + pbl->type = MB_DT_DICT_IT; + pbl->value.dict_it = itn->data.dict_it; + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + pbl->type = MB_DT_CLASS; + pbl->value.instance = itn->data.instance; + + break; +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + pbl->type = MB_DT_ROUTINE; + pbl->value.routine = itn->data.routine; + + break; + default: + result = MB_FUNC_ERR; + + break; + } + + return result; +} + +/* Create an internal object from a public value */ +static int _create_internal_object_from_public_value(mb_value_t* pbl, _object_t** itn) { + int result = MB_FUNC_OK; + + mb_assert(pbl && itn); + + *itn = _create_object(); + _public_value_to_internal_object(pbl, *itn); + if((*itn)->type == _DT_STRING) { + (*itn)->ref = false; + (*itn)->data.string = mb_strdup((*itn)->data.string, 0); + } + + return result; +} + +/* Compare a public value and an internal object */ +static int _compare_public_value_and_internal_object(mb_value_t* pbl, _object_t* itn) { + int result = 0; + mb_value_t tmp; + + mb_make_nil(tmp); + _internal_object_to_public_value(itn, &tmp); + if(pbl->type != tmp.type) + result = pbl->type - tmp.type; + else + result = mb_memcmp(pbl->value.bytes, tmp.value.bytes, sizeof(mb_val_bytes_t)); + + return result; +} + +/* Try clear the intermediate value */ +static void _try_clear_intermediate_value(void* data, void* extra, mb_interpreter_t* s) { + _object_t* obj = 0; + _running_context_t* running = 0; + mb_unrefvar(extra); + + mb_assert(s); + + if(data == 0) + return; + + obj = (_object_t*)data; + running = s->running_context; + + if(!_compare_public_value_and_internal_object(&running->intermediate_value, obj)) { + mb_make_nil(running->intermediate_value); + } +} + +/* Remove from another list if exist */ +static void _remove_if_exist(void* data, void* extra, _ls_node_t* ls) { + _object_t* obj = 0; + mb_unrefvar(extra); + + obj = (_object_t*)data; + _ls_try_remove(ls, obj, _ls_cmp_data, 0); +} + +/* Destroy an object in variable argument list */ +static void _destroy_var_arg(void* data, void* extra, _gc_t* gc) { + _object_t* obj = 0; + mb_unrefvar(extra); + mb_unrefvar(gc); + + mb_assert(data); + + obj = (_object_t*)data; + _UNREF(obj); + safe_free(obj); +} + +/* Destroy edge destroying objects */ +static void _destroy_edge_objects(mb_interpreter_t* s) { + mb_assert(s); + + _ls_foreach(s->edge_destroy_objects, _destroy_object); + _ls_clear(s->edge_destroy_objects); +} + +/* Mark a string as an edge destroying object */ +static void _mark_edge_destroy_string(mb_interpreter_t* s, char* ch) { + _object_t* temp_obj = 0; + + mb_assert(s && ch); + + temp_obj = _create_object(); + temp_obj->type = _DT_STRING; + temp_obj->ref = false; + temp_obj->data.string = ch; + _ls_pushback(s->edge_destroy_objects, temp_obj); +} + +/* Destroy lazy destroying objects */ +static void _destroy_lazy_objects(mb_interpreter_t* s) { + mb_assert(s); + + _ls_foreach(s->lazy_destroy_objects, _destroy_object); + _ls_clear(s->lazy_destroy_objects); +} + +/* Mark a string as a lazy destroying object */ +static void _mark_lazy_destroy_string(mb_interpreter_t* s, char* ch) { + _object_t* temp_obj = 0; + + mb_assert(s && ch); + + temp_obj = _create_object(); + temp_obj->type = _DT_STRING; + temp_obj->ref = false; + temp_obj->data.string = ch; + _ls_pushback(s->lazy_destroy_objects, temp_obj); +} + +/* Assign a value with another */ +static void _assign_public_value(mb_value_t* tgt, mb_value_t* src) { + _object_t obj; + mb_value_t nil; + + mb_assert(tgt); + + _MAKE_NIL(&obj); + _public_value_to_internal_object(tgt, &obj); + _UNREF(&obj) + + mb_make_nil(nil); + if(!src) + src = &nil; + memcpy(tgt, src, sizeof(mb_value_t)); + *src = nil; +} + +/* Swap two public values */ +static void _swap_public_value(mb_value_t* tgt, mb_value_t* src) { + mb_value_t tmp; + + mb_assert(tgt && src); + + tmp = *tgt; + *tgt = *src; + *src = tmp; +} + +/* Clear the scope chain */ +static int _clear_scope_chain(mb_interpreter_t* s) { + int result = 0; + _running_context_t* running = 0; + _running_context_t* prev = 0; + + mb_assert(s); + + running = s->running_context; + while(running) { + prev = running->prev; + + _ht_foreach(running->var_dict, _destroy_object); + _ht_clear(running->var_dict); +#ifdef MB_ENABLE_LAMBDA + if(running->refered_lambdas) + _ls_clear(running->refered_lambdas); +#endif /* MB_ENABLE_LAMBDA */ + + result++; + running = prev; + } + + return result; +} + +/* Dispose the scope chain */ +static int _dispose_scope_chain(mb_interpreter_t* s) { + int result = 0; + _running_context_t* running = 0; + _running_context_t* prev = 0; + + mb_assert(s); + + running = s->running_context; + while(running) { + prev = running->prev; + + _ht_foreach(running->var_dict, _destroy_object); + _ht_clear(running->var_dict); + _ht_destroy(running->var_dict); +#ifdef MB_ENABLE_LAMBDA + if(running->refered_lambdas) { + _ls_clear(running->refered_lambdas); + _ls_destroy(running->refered_lambdas); + running->refered_lambdas = 0; + } +#endif /* MB_ENABLE_LAMBDA */ + running->var_dict = 0; + mb_dispose_value(s, running->intermediate_value); + safe_free(running); + + result++; + running = prev; + } + s->running_context = 0; + + return result; +} + +/* Tidy the scope chain */ +static void _tidy_scope_chain(mb_interpreter_t* s) { + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + if(!context) + return; + + while(context->routine_state && s->running_context->meta != _SCOPE_META_ROOT) { + if(_end_routine(s)) + _pop_scope(s, false); + } +#ifdef MB_ENABLE_CLASS + while(context->class_state != _CLASS_STATE_NONE) { + if(_end_class(s)) + _pop_scope(s, false); + } +#endif /* MB_ENABLE_CLASS */ +} + +/* Tidy the intermediate value */ +static void _tidy_intermediate_value(_ref_t* ref, void* data) { + _object_t tmp; + + mb_assert(ref && data); + + if(!ref->s->running_context) + return; + + _MAKE_NIL(&tmp); + _public_value_to_internal_object(&ref->s->running_context->intermediate_value, &tmp); + if(tmp.data.usertype == data) { + switch(tmp.type) { +#ifdef MB_ENABLE_COLLECTION_LIB + case _DT_LIST: /* Fall through */ + case _DT_DICT: /* Fall through */ +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: /* Fall through */ +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_USERTYPE_REF + case _DT_USERTYPE_REF: /* Fall through */ +#endif /* MB_ENABLE_USERTYPE_REF */ + case _DT_ARRAY: + mb_make_nil(ref->s->running_context->intermediate_value); + + break; + default: /* Do nothing */ + break; + } + } +} + +/* Evaluate a variable, this is a helper function for the PRINT statement */ +static _object_t* _eval_var_in_print(mb_interpreter_t* s, _object_t** val_ptr, _ls_node_t** ast, _object_t* obj) { + _object_t tmp; + + mb_assert(s); + + switch(obj->type) { + case _DT_ROUTINE: + _execute_statement(s, ast, true); + _MAKE_NIL(&tmp); + _public_value_to_internal_object(&s->running_context->intermediate_value, &tmp); + if(tmp.type == _DT_STRING) { + tmp.data.string = mb_strdup(tmp.data.string, strlen(tmp.data.string) + 1); + tmp.ref = false; + mb_make_nil(s->running_context->intermediate_value); + } + **val_ptr = tmp; + if(*ast) *ast = (*ast)->prev; + + break; + case _DT_VAR: + *val_ptr = obj->data.variable->data; + if(*ast) *ast = (*ast)->next; + + break; + default: + *val_ptr = obj; + if(*ast) *ast = (*ast)->next; + + break; + } + + return *val_ptr; +} + +/** Interpretation */ + +/* Callback a stepped debug handler, this function is called each step */ +static void _stepped(mb_interpreter_t* s, _ls_node_t* ast) { + _object_t* obj = 0; + + mb_assert(s); + + if(s->debug_stepped_handler) { + if(ast && ast->data) { + obj = (_object_t*)ast->data; +#ifdef MB_ENABLE_SOURCE_TRACE + s->debug_stepped_handler(s, (void**)&ast, s->source_file, obj->source_pos, obj->source_row, obj->source_col); +#else /* MB_ENABLE_SOURCE_TRACE */ + s->debug_stepped_handler(s, (void**)&ast, s->source_file, obj->source_pos, 0, 0); +#endif /* MB_ENABLE_SOURCE_TRACE */ + } else { + s->debug_stepped_handler(s, (void**)&ast, s->source_file, -1, 0, 0); + } + } +} + +/* Execute the ast, this is the core execution function */ +static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l, bool_t force_next) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _running_context_t* running = 0; + _ls_node_t* sub_stack = 0; + bool_t skip_to_eoi = true; + + mb_assert(s && l); + + running = s->running_context; + sub_stack = s->sub_stack; + + ast = *l; + + obj = (_object_t*)ast->data; + +_retry: + switch(obj->type) { + case _DT_FUNC: + if(_is_binary(obj->data.func->pointer)) { + _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(_is_flow(obj->data.func->pointer)) { + result = (obj->data.func->pointer)(s, (void**)&ast); + } else { +#ifdef MB_ENABLE_STACK_TRACE + _ls_pushback(s->stack_frames, obj->data.func->name); +#endif /* MB_ENABLE_STACK_TRACE */ + result = (obj->data.func->pointer)(s, (void**)&ast); +#ifdef MB_ENABLE_STACK_TRACE + _ls_popback(s->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + } + if(result == MB_FUNC_IGNORE) { + result = MB_FUNC_OK; + obj = (_object_t*)ast->data; + + goto _retry; + } + + break; + case _DT_VAR: +#ifdef MB_ENABLE_CLASS + if(obj->data.variable->data->type == _DT_ROUTINE) { + if(ast && ast->next && _IS_FUNC(ast->next->data, _core_open_bracket)) { + obj = obj->data.variable->data; + + goto _retry; + } else { + result = _core_let(s, (void**)&ast); + } + } else if(s->last_instance || obj->data.variable->pathing) { + /* Need to path */ + _ls_node_t* pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PU(obj->data.variable->pathing), 0, 0); + if(pathed && pathed->data) { + if(obj != (_object_t*)pathed->data) { + /* Found another node */ + obj = (_object_t*)pathed->data; + + goto _retry; + } else { + /* Final node */ + result = _core_let(s, (void**)&ast); + } + } else { + /* Normal node */ + result = _core_let(s, (void**)&ast); + } + } else { + /* Do not need to path */ + result = _core_let(s, (void**)&ast); + } +#else /* MB_ENABLE_CLASS */ + result = _core_let(s, (void**)&ast); +#endif /* MB_ENABLE_CLASS */ + + break; + case _DT_ARRAY: + result = _core_let(s, (void**)&ast); + + break; + case _DT_INT: /* Fall through */ + case _DT_REAL: /* Fall through */ + case _DT_STRING: + _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + + break; +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: + _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + + break; +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + ast = ast->prev; + result = _core_call(s, (void**)&ast); + + break; +#ifdef MB_ENABLE_SOURCE_TRACE + case _DT_PREV_IMPORT: + s->source_file = obj->data.import_info->source_file; + + break; + case _DT_POST_IMPORT: + s->source_file = obj->data.import_info->source_file; + + break; +#endif /* MB_ENABLE_SOURCE_TRACE */ + default: /* Do nothing */ + break; + } + + if(s->schedule_suspend_tag) { + if(s->schedule_suspend_tag == MB_FUNC_SUSPEND) + mb_suspend(s, (void**)&ast); + result = s->schedule_suspend_tag; + s->schedule_suspend_tag = 0; + } + + if(result != MB_FUNC_OK && result != MB_FUNC_SUSPEND && result != MB_SUB_RETURN) + goto _exit; + + if(ast) { + obj = DON(ast); + if(!obj) { + /* Do nothing */ + } else if(_IS_EOS(obj)) { + if(force_next || result != MB_SUB_RETURN) + ast = ast->next; + } else if(_IS_SEP(obj, ':')) { + skip_to_eoi = false; + ast = ast->next; + } else if(obj && obj->type == _DT_VAR) { +#ifdef MB_ENABLE_CLASS + _ls_node_t* fn = 0; + if(_is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { + if(fn) { + if(s->calling) + result = _core_let(s, (void**)&ast); + } else { + _handle_error_on_obj(s, SE_RN_INVALID_ID_USAGE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + } else { + _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } +#else /* MB_ENABLE_CLASS */ + _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_CLASS */ + } else if(_IS_FUNC(obj, _core_enddef) && result != MB_SUB_RETURN) { + ast = (_ls_node_t*)_ls_popback(sub_stack); +#ifdef MB_ENABLE_LAMBDA + } else if(obj && _IS_FUNC(obj, _core_close_bracket) && s->last_routine && s->last_routine->type == _IT_LAMBDA) { + /* Do nothing */ +#endif /* MB_ENABLE_LAMBDA */ + } else if(obj && obj->type == _DT_FUNC && (_is_operator(obj->data.func->pointer) || _is_flow(obj->data.func->pointer))) { + ast = ast->next; + } else if(obj && obj->type == _DT_FUNC) { + /* Do nothing */ + } else if(obj && obj->type != _DT_FUNC) { + ast = ast->next; + } else { + _handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + } + + if(skip_to_eoi && s->skip_to_eoi && s->skip_to_eoi == _ls_back(s->sub_stack)) { + s->skip_to_eoi = 0; + obj = (_object_t*)ast->data; + if(obj->type != _DT_EOS) { + result = _skip_to(s, &ast, 0, _DT_EOS); + if(result != MB_FUNC_OK) + goto _exit; + } + } + +_exit: + _destroy_lazy_objects(s); + + *l = ast; + + _stepped(s, ast); + + return result; +} + +/* Common function to end current looping */ +static int _common_end_looping(mb_interpreter_t* s, _ls_node_t** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + result = _skip_struct(s, l, _core_for, _core_next); + if(result == MB_FUNC_OK) + result = _skip_to(s, l, 0, _DT_EOS); + + return result; +} + +/* Common function to keep current looping */ +static int _common_keep_looping(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _running_context_t* running = 0; + + mb_assert(s && l); + + running = s->running_context; + ast = *l; + + obj = (_object_t*)ast->data; + while(!_IS_FUNC(obj, _core_next)) { + result = _execute_statement(s, &ast, false); + if(result == MB_LOOP_CONTINUE) { /* NEXT */ + if(!running->next_loop_var || running->next_loop_var == var_loop) { /* This loop */ + running->next_loop_var = 0; + result = MB_FUNC_OK; + + break; + } else { /* Not this loop */ + if(_skip_struct(s, &ast, _core_for, _core_next) != MB_FUNC_OK) + goto _exit; + _skip_to(s, &ast, 0, _DT_EOS); + + goto _exit; + } + } else if(result == MB_LOOP_BREAK) { /* EXIT */ + if(_skip_struct(s, &ast, _core_for, _core_next) != MB_FUNC_OK) + goto _exit; + _skip_to(s, &ast, 0, _DT_EOS); + + goto _exit; + } else if(result == MB_SUB_RETURN) { /* RETURN */ + goto _exit; + } else if(result != MB_FUNC_OK) { /* Normally */ + goto _exit; + } + + if(!ast) { + _handle_error_on_obj(s, SE_RN_NEXT_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + obj = (_object_t*)ast->data; + } + +_exit: + *l = ast; + + return result; +} + +/* Execute normal FOR-TO-STEP-NEXT-routine */ +static int _execute_normal_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* to_node = 0; + _object_t* obj = 0; + _object_t to_val; + _object_t step_val; + _object_t* to_val_ptr = 0; + _object_t* step_val_ptr = 0; + _tuple3_t ass_tuple3; + _tuple3_t* ass_tuple3_ptr = 0; + + mb_assert(s && l && var_loop); + + ast = *l; + + to_val_ptr = &to_val; + _MAKE_NIL(to_val_ptr); + step_val_ptr = &step_val; + _MAKE_NIL(step_val_ptr); + ass_tuple3_ptr = &ass_tuple3; + + /* Get begin value */ + result = _execute_statement(s, &ast, true); + if(result != MB_FUNC_OK) + goto _exit; + ast = ast->prev; + + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_to)) { + _handle_error_on_obj(s, SE_RN_TO_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + ast = ast->next; + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + to_node = ast; + +_to: + ast = to_node; + + /* Get end value */ + result = _calc_expression(s, &ast, &to_val_ptr); + if(result != MB_FUNC_OK) + goto _exit; + + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_step)) { + step_val = _OBJ_INT_UNIT; + } else { + ast = ast->next; + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + /* Get step value */ + result = _calc_expression(s, &ast, &step_val_ptr); + if(result != MB_FUNC_OK) + goto _exit; + } + + if((_compare_numbers(step_val_ptr, &_OBJ_INT_ZERO) == 1 && _compare_numbers(var_loop->data, to_val_ptr) == 1) || + (_compare_numbers(step_val_ptr, &_OBJ_INT_ZERO) == -1 && _compare_numbers(var_loop->data, to_val_ptr) == -1)) { + /* End looping */ + result = _common_end_looping(s, &ast); + + goto _exit; + } else { + /* Keep looping */ + result = _common_keep_looping(s, &ast, var_loop); + if(result == MB_LOOP_BREAK) { + result = MB_FUNC_OK; + + goto _exit; + } else if(result != MB_FUNC_OK || result == MB_SUB_RETURN) { + goto _exit; + } + + ass_tuple3.e1 = var_loop->data; + ass_tuple3.e2 = step_val_ptr; + ass_tuple3.e3 = var_loop->data; + _instruct_num_op_num(+, &ass_tuple3_ptr); + + goto _to; + } + +_exit: + *l = ast; + + return result; +} + +#ifdef MB_ENABLE_COLLECTION_LIB +/* Execute ranged FOR-IN-NEXT-routine */ +static int _execute_ranged_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) { + int result = MB_FUNC_ERR; + _ls_node_t* ast = 0; + _object_t* old_val = 0; + _object_t range; + _ls_node_t* to_node = 0; + _object_t* range_ptr; + _list_it_t* lit = 0; + _dict_it_t* dit = 0; + _list_it_t* tlit = 0; + _dict_it_t* tdit = 0; + + mb_assert(s && l && var_loop); + + old_val = var_loop->data; + range_ptr = ⦥ + _MAKE_NIL(range_ptr); + + ast = *l; + ast = ast->next; + ast = ast->next; + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + /* Get collection */ + result = _calc_expression(s, &ast, &range_ptr); + if(result != MB_FUNC_OK) + goto _exit; + + /* Create iterator */ + switch(range_ptr->type) { + case _DT_LIST: + tlit = lit = _create_list_it(range_ptr->data.list, true); + + break; + case _DT_DICT: + tdit = dit = _create_dict_it(range_ptr->data.dict, true); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + + break; + } + to_node = ast; + +_to: + ast = to_node; + + /* Move next */ + if(lit) lit = _move_list_it_next(lit); + else if(dit) dit = _move_dict_it_next(dit); + if((lit && _invalid_list_it(lit)) || (dit && _invalid_dict_it(dit))) { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(!lit && !dit) { + /* End looping */ + result = _common_end_looping(s, &ast); + + goto _exit; + } else { + /* Assign loop variable */ + if(lit && !lit->list->range_begin && lit->curr.node && lit->curr.node->data) { + var_loop->data = (_object_t*)lit->curr.node->data; + } else if(lit && lit->list->range_begin) { + var_loop->data->type = _DT_INT; + var_loop->data->data.integer = lit->curr.ranging; + } else if(dit && dit->curr_node && dit->curr_node->extra) { + var_loop->data = (_object_t*)dit->curr_node->extra; + } + /* Keep looping */ + result = _common_keep_looping(s, &ast, var_loop); + if(result == MB_LOOP_BREAK) { + result = MB_FUNC_OK; + + goto _exit; + } else if(result != MB_FUNC_OK || result == MB_SUB_RETURN) { + goto _exit; + } + + goto _to; + } + +_exit: + if(tlit) _destroy_list_it(tlit); + else if(tdit) _destroy_dict_it(tdit); + switch(range_ptr->type) { _UNREF_COLL(range_ptr) default: /* Do nothing */ break; } + + *l = ast; + + var_loop->data = old_val; + + return result; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* Skip current execution flow to a specific function */ +static int _skip_to(mb_interpreter_t* s, _ls_node_t** l, mb_func_t f, _data_e t) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* tmp = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + ast = *l; + mb_assert(ast && ast->prev); + do { + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(tmp), MB_FUNC_ERR, _exit, result); + } + tmp = ast; + obj = (_object_t*)ast->data; + *l = ast; + ast = ast->next; + } while(!(_IS_FUNC(obj, f)) && obj->type != t); + +_exit: + return result; +} + +/* Skip current IF execution flow to next chunk */ +static int _skip_if_chunk(mb_interpreter_t* s, _ls_node_t** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* tmp = 0; + _object_t* obj = 0; + int nested = 0; + unsigned mask = 0; + + mb_assert(s && l); + + ast = *l; + mb_assert(ast && ast->prev); + do { + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + tmp = ast; + obj = (_object_t*)ast->data; + *l = ast; + ast = ast->next; + if(ast && _IS_FUNC((_object_t*)ast->data, _core_if)) { + if(++nested > sizeof(mask) * 8) { + _handle_error_on_obj(s, SE_RN_NESTED_TOO_DEEP, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } else if(ast && nested && _IS_FUNC((_object_t*)ast->data, _core_then)) { + if(!(ast && ast->next && _IS_EOS(ast->next->data))) + mask |= 1 << (nested - 1); + } else if(ast && nested && + (((mask & (1 << (nested - 1))) && _IS_EOS(ast->data)) || + (!(mask & (1 << (nested - 1))) && _IS_FUNC((_object_t*)ast->data, _core_endif))) + ) { + if(--nested < 0) { + _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + } + } while(nested || (!_IS_FUNC(obj, _core_elseif) && !_IS_FUNC(obj, _core_else) && !_IS_FUNC(obj, _core_endif))); + +_exit: + return result; +} + +/* Skip current structure */ +static int _skip_struct(mb_interpreter_t* s, _ls_node_t** l, mb_func_t open_func, mb_func_t close_func) { + int result = MB_FUNC_OK; + int count = 0; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _object_t* obj_prev = 0; + + mb_assert(s && l && open_func && close_func); + + ast = *l; + + count = 1; + do { + if(!ast || !ast->next) { + _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + obj_prev = (_object_t*)ast->data; + ast = ast->next; + obj = (_object_t*)ast->data; + if(_IS_FUNC(obj, open_func)) + ++count; + else if(_IS_FUNC(obj, close_func) && _IS_EOS(obj_prev)) + --count; + } while(count); + +_exit: + *l = ast; + + return result; +} + +/* Create a running context */ +static _running_context_t* _create_running_context(bool_t create_var_dict) { + _running_context_t* result = 0; + + result = (_running_context_t*)mb_malloc(sizeof(_running_context_t)); + memset(result, 0, sizeof(_running_context_t)); + result->calc_depth = _INFINITY_CALC_DEPTH; + if(create_var_dict) + result->var_dict = _ht_create(0, _ht_cmp_string, _ht_hash_string, 0); + + return result; +} + +/* Reset the parsing context of a MY-BASIC environment */ +static _parsing_context_t* _reset_parsing_context(_parsing_context_t* context) { + _ls_node_t* imp = 0; + + if(!context) + context = (_parsing_context_t*)mb_malloc(sizeof(_parsing_context_t)); + else + imp = context->imported; + memset(context, 0, sizeof(_parsing_context_t)); + context->parsing_row = 1; + if(!imp) + imp = _ls_create(); + context->imported = imp; + _end_of_file(context); + + return context; +} + +/* Destroy the parsing context of a MY-BASIC environment */ +static void _destroy_parsing_context(_parsing_context_t* _mb_unaligned * context) { + if(!context || !(*context)) + return; + + if(*context) { + if((*context)->imported) { + _ls_foreach((*context)->imported, _destroy_memory); + _ls_destroy((*context)->imported); + } + safe_free(*context); + } +} + +/** Interface processors */ + +#ifdef MB_ENABLE_MODULE +/* Create a module function structure */ +static _module_func_t* _create_module_func(mb_interpreter_t* s, mb_func_t f) { + _module_func_t* result = 0; + + mb_assert(s); + + if(!s->with_module) + return result; + + result = (_module_func_t*)mb_malloc(sizeof(_module_func_t)); + result->module = mb_strdup(s->with_module, 0); + result->func = f; + + return result; +} + +/* Destroy a module function structure */ +static int _ls_destroy_module_func(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _module_func_t* mod = 0; + mb_unrefvar(extra); + + mb_assert(data); + + mod = (_module_func_t*)data; + safe_free(mod->module); + safe_free(mod); + + return result; +} + +/* Destroy all module function structures */ +static int _ht_destroy_module_func_list(void* data, void* extra) { + int result = _OP_RESULT_DEL_NODE; + _ls_node_t* lst = 0; + char* n = 0; + + mb_assert(data); + + lst = (_ls_node_t*)data; + n = (char*)extra; + _ls_foreach(lst, _ls_destroy_module_func); + _ls_destroy(lst); + safe_free(n); + + return result; +} +#endif /* MB_ENABLE_MODULE */ + +/* Generate a function name to be registered according to module information */ +static char* _generate_func_name(mb_interpreter_t* s, char* n, bool_t with_mod) { + char* name = 0; + size_t _sl = 0; + mb_unrefvar(with_mod); + + mb_assert(s && n); + + _sl = strlen(n); +#ifdef MB_ENABLE_MODULE + if(with_mod && s->with_module) { + size_t _ml = strlen(s->with_module); + name = (char*)mb_malloc(_ml + 1 + _sl + 1); + memcpy(name, s->with_module, _ml); + name[_ml] = '.'; + memcpy(name + _ml + 1, n, _sl + 1); + } else { + name = (char*)mb_malloc(_sl + 1); + memcpy(name, n, _sl + 1); + } +#else /* MB_ENABLE_MODULE */ + name = (char*)mb_malloc(_sl + 1); + memcpy(name, n, _sl + 1); +#endif /* MB_ENABLE_MODULE */ + mb_strupr(name); + + return name; +} + +/* Register a function to a MY-BASIC environment */ +static int _register_func(mb_interpreter_t* s, char* n, mb_func_t f, bool_t local) { + int result = 0; + _ht_node_t* scope = 0; + _ls_node_t* exists = 0; + char* name = 0; + + mb_assert(s); + + if(!n) + return result; + + n = mb_strdup(n, strlen(n) + 1); + mb_strupr(n); + + scope = local ? s->local_func_dict : s->global_func_dict; +#ifdef MB_ENABLE_MODULE + if(s->with_module) + name = _generate_func_name(s, n, true); +#endif /* MB_ENABLE_MODULE */ + if(!name) + name = mb_strdup(n, strlen(n) + 1); + exists = _ht_find(scope, (void*)name); + if(!exists) { + result += _ht_set_or_insert(scope, (void*)name, (void*)(intptr_t)f); + } else { + _set_current_error(s, SE_CM_FUNC_EXISTS, 0); + safe_free(name); + } + +#ifdef MB_ENABLE_MODULE + if(s->with_module) { + _ls_node_t* tmp = 0; + exists = _ht_find(s->module_func_dict, (void*)n); + if(!exists) { + name = _generate_func_name(s, n, false); + result += _ht_set_or_insert(s->module_func_dict, (void*)name, _ls_create()); + } + exists = _ht_find(s->module_func_dict, (void*)n); + exists = (_ls_node_t*)exists->data; + tmp = _ls_find(exists, s, _ls_cmp_module_func, 0); + if(!tmp) + _ls_pushback(exists, _create_module_func(s, f)); + else + _set_current_error(s, SE_CM_FUNC_EXISTS, 0); + } +#endif /* MB_ENABLE_MODULE */ + + safe_free(n); + + return result; +} + +/* Remove a function from a MY-BASIC environment */ +static int _remove_func(mb_interpreter_t* s, char* n, bool_t local) { + int result = 0; + _ht_node_t* scope = 0; + _ls_node_t* exists = 0; + char* name = 0; + + mb_assert(s); + + if(!n) + return result; + + n = mb_strdup(n, strlen(n) + 1); + mb_strupr(n); + + scope = local ? s->local_func_dict : s->global_func_dict; +#ifdef MB_ENABLE_MODULE + if(s->with_module) + name = _generate_func_name(s, n, true); +#endif /* MB_ENABLE_MODULE */ + if(!name) + name = mb_strdup(n, strlen(n) + 1); + exists = _ht_find(scope, (void*)name); + if(exists) + result += _ht_remove(scope, (void*)name, _ls_cmp_extra_string); + else + _set_current_error(s, SE_CM_FUNC_NOT_EXISTS, 0); + safe_free(name); + +#ifdef MB_ENABLE_MODULE + if(s->with_module) { + _ls_node_t* tmp = 0; + exists = _ht_find(s->module_func_dict, (void*)n); + if(exists) { + exists = (_ls_node_t*)exists->data; + tmp = _ls_find(exists, s, _ls_cmp_module_func, 0); + if(tmp) + _ls_remove(exists, tmp, _ls_destroy_module_func); + } + } +#endif /* MB_ENABLE_MODULE */ + + safe_free(n); + + return result; +} + +/* Find function interface in the function dictionaries */ +static _ls_node_t* _find_func(mb_interpreter_t* s, char* n, bool_t* mod) { + _ls_node_t* result = 0; + mb_unrefvar(mod); + + mb_assert(s && n); + + n = mb_strdup(n, strlen(n) + 1); + mb_strupr(n); + + result = _ht_find(s->local_func_dict, (void*)n); + if(!result) + result = _ht_find(s->global_func_dict, (void*)n); + +#ifdef MB_ENABLE_MODULE + if(!result) { + result = _ht_find(s->module_func_dict, (void*)n); + if(result && result->data) { + _module_func_t* mp = 0; + result = (_ls_node_t*)result->data; + result = result->next; + while(result) { + mp = (_module_func_t*)result->data; + if(_ls_find(s->using_modules, mp->module, (_ls_compare)_ht_cmp_string, 0)) + break; + result = result->next; + } + *mod = true; + } + } +#endif /* MB_ENABLE_MODULE */ + + safe_free(n); + + return result; +} + +/* Open global constants */ +static int _open_constant(mb_interpreter_t* s) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + unsigned long ul = 0; + + mb_assert(s); + + running = s->running_context; + + ul = _ht_set_or_insert(running->var_dict, MB_TRUE, _OBJ_BOOL_TRUE); + mb_assert(ul); + ul = _ht_set_or_insert(running->var_dict, MB_FALSE, _OBJ_BOOL_FALSE); + mb_assert(ul); + + return result; +} + +/* Close global constants */ +static int _close_constant(mb_interpreter_t* s) { + int result = MB_FUNC_OK; + + mb_assert(s); + + return result; +} + +/* Open the core function library */ +static int _open_core_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_core_libs); ++i) + result += _register_func(s, _core_libs[i].name, _core_libs[i].pointer, true); + + return result; +} + +/* Close the core function library */ +static int _close_core_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_core_libs); ++i) + result += _remove_func(s, _core_libs[i].name, true); + + return result; +} + +/* Open the standard function library */ +static int _open_std_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_std_libs); ++i) + result += _register_func(s, _std_libs[i].name, _std_libs[i].pointer, true); + + return result; +} + +/* Close the standard function library */ +static int _close_std_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_std_libs); ++i) + result += _remove_func(s, _std_libs[i].name, true); + + return result; +} + +#ifdef MB_ENABLE_COLLECTION_LIB +/* Open the collection function library */ +static int _open_coll_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_coll_libs); ++i) + result += _register_func(s, _coll_libs[i].name, _coll_libs[i].pointer, true); + + return result; +} + +/* Close the collection function library */ +static int _close_coll_lib(mb_interpreter_t* s) { + int result = 0; + int i = 0; + + mb_assert(s); + + for(i = 0; i < countof(_coll_libs); ++i) + result += _remove_func(s, _coll_libs[i].name, true); + + return result; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* ========================================================} */ + +/* +** {======================================================== +** Public functions definitions +*/ + +/* Get the version number of this MY-BASIC system */ +unsigned mb_ver(void) { + return _MB_VERSION; +} + +/* Get the version text of this MY-BASIC system */ +const char* mb_ver_string(void) { + return _MB_VERSION_STRING; +} + +/* Initialize the MY-BASIC system */ +int mb_init(void) { + int result = MB_FUNC_OK; + + mb_assert(!_exp_assign); + _exp_assign = _create_object(); + _exp_assign->type = _DT_FUNC; + _exp_assign->data.func = (_func_t*)mb_malloc(sizeof(_func_t)); + memset(_exp_assign->data.func, 0, sizeof(_func_t)); + _exp_assign->data.func->name = (char*)mb_malloc(strlen(_DUMMY_ASSIGN_CHAR) + 1); + memcpy(_exp_assign->data.func->name, _DUMMY_ASSIGN_CHAR, strlen(_DUMMY_ASSIGN_CHAR) + 1); + _exp_assign->data.func->pointer = _core_dummy_assign; + + mb_assert(!_OBJ_BOOL_TRUE); + if(!_OBJ_BOOL_TRUE) { + _var_t* bvar = _create_var(&_OBJ_BOOL_TRUE, MB_TRUE, strlen(MB_TRUE) + 1, true); + bvar->data->type = _DT_INT; + bvar->data->data.integer = 1; + } + mb_assert(!_OBJ_BOOL_FALSE); + if(!_OBJ_BOOL_FALSE) { + _var_t* bvar = _create_var(&_OBJ_BOOL_FALSE, MB_FALSE, strlen(MB_FALSE) + 1, true); + bvar->data->type = _DT_INT; + bvar->data->data.integer = 0; + } + + return result; +} + +/* Close the MY-BASIC system */ +int mb_dispose(void) { + int result = MB_FUNC_OK; + + mb_assert(_exp_assign); + safe_free(_exp_assign->data.func->name); + safe_free(_exp_assign->data.func); + safe_free(_exp_assign); + _exp_assign = 0; + + mb_assert(_OBJ_BOOL_TRUE); + if(_OBJ_BOOL_TRUE) { + safe_free(_OBJ_BOOL_TRUE->data.variable->data); + safe_free(_OBJ_BOOL_TRUE->data.variable->name); + safe_free(_OBJ_BOOL_TRUE->data.variable); + safe_free(_OBJ_BOOL_TRUE); + _OBJ_BOOL_TRUE = 0; + } + mb_assert(_OBJ_BOOL_FALSE); + if(_OBJ_BOOL_FALSE) { + safe_free(_OBJ_BOOL_FALSE->data.variable->data); + safe_free(_OBJ_BOOL_FALSE->data.variable->name); + safe_free(_OBJ_BOOL_FALSE->data.variable); + safe_free(_OBJ_BOOL_FALSE); + _OBJ_BOOL_FALSE = 0; + } + + return result; +} + +/* Open a MY-BASIC environment */ +int mb_open(struct mb_interpreter_t** s) { + int result = MB_FUNC_OK; + _ht_node_t* local_scope = 0; + _ht_node_t* global_scope = 0; + _running_context_t* running = 0; + + mb_assert(s); + + *s = (mb_interpreter_t*)mb_malloc(sizeof(mb_interpreter_t)); + memset(*s, 0, sizeof(mb_interpreter_t)); + (*s)->valid = true; + + (*s)->in_neg_expr = _ls_create(); + + local_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ls_free_extra); + (*s)->local_func_dict = local_scope; + + global_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ls_free_extra); + (*s)->global_func_dict = global_scope; + +#ifdef MB_ENABLE_MODULE + global_scope = _ht_create(0, _ht_cmp_string, _ht_hash_string, _ht_destroy_module_func_list); + (*s)->module_func_dict = global_scope; + (*s)->using_modules = _ls_create(); +#endif /* MB_ENABLE_MODULE */ + + (*s)->parsing_context = _reset_parsing_context((*s)->parsing_context); + + (*s)->edge_destroy_objects = _ls_create(); + (*s)->lazy_destroy_objects = _ls_create(); + + (*s)->gc.table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + (*s)->gc.recursive_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + (*s)->gc.collected_table = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object); + (*s)->gc.valid_table = 0; + (*s)->gc.collecting = 0; + + running = _create_running_context(true); + running->meta = _SCOPE_META_ROOT; + (*s)->running_context = running; + +#ifdef MB_ENABLE_STACK_TRACE + (*s)->stack_frames = _ls_create(); +#endif /* MB_ENABLE_STACK_TRACE */ + + (*s)->sub_stack = _ls_create(); + + (*s)->ast = _ls_create(); + + _open_core_lib(*s); + _open_std_lib(*s); +#ifdef MB_ENABLE_COLLECTION_LIB + _open_coll_lib(*s); +#endif /* MB_ENABLE_COLLECTION_LIB */ + + result = _open_constant(*s); + mb_assert(MB_FUNC_OK == result); + + return result; +} + +/* Close a MY-BASIC environment */ +int mb_close(struct mb_interpreter_t** s) { + int result = MB_FUNC_OK; + _ht_node_t* local_scope = 0; + _ht_node_t* global_scope = 0; + _ls_node_t* ast; + + mb_assert(s); + + (*s)->valid = false; + +#ifdef MB_ENABLE_COLLECTION_LIB + _close_coll_lib(*s); +#endif /* MB_ENABLE_COLLECTION_LIB */ + _close_std_lib(*s); + _close_core_lib(*s); + + ast = (*s)->ast; + _ls_foreach(ast, _destroy_object); + _ls_destroy(ast); + + _ls_destroy((*s)->sub_stack); + +#ifdef MB_ENABLE_STACK_TRACE + _ls_destroy((*s)->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + + _tidy_scope_chain(*s); + _dispose_scope_chain(*s); + + _gc_collect_garbage(*s, -1); + _ht_destroy((*s)->gc.table); + _ht_destroy((*s)->gc.recursive_table); + _ht_destroy((*s)->gc.collected_table); + (*s)->gc.table = 0; + (*s)->gc.recursive_table = 0; + (*s)->gc.collected_table = 0; + + _ls_foreach((*s)->edge_destroy_objects, _destroy_object); + _ls_destroy((*s)->edge_destroy_objects); + _ls_foreach((*s)->lazy_destroy_objects, _destroy_object); + _ls_destroy((*s)->lazy_destroy_objects); + + _destroy_parsing_context(&(*s)->parsing_context); + +#ifdef MB_ENABLE_MODULE + global_scope = (*s)->module_func_dict; + _ht_foreach(global_scope, _ht_destroy_module_func_list); + _ht_destroy(global_scope); + _ls_foreach((*s)->using_modules, _destroy_memory); + _ls_destroy((*s)->using_modules); +#endif /* MB_ENABLE_MODULE */ + + global_scope = (*s)->global_func_dict; + _ht_foreach(global_scope, _ls_free_extra); + _ht_destroy(global_scope); + + local_scope = (*s)->local_func_dict; + _ht_foreach(local_scope, _ls_free_extra); + _ht_destroy(local_scope); + + _ls_destroy((*s)->in_neg_expr); + + _close_constant(*s); + + safe_free(*s); + + return result; +} + +/* Reset a MY-BASIC environment */ +int mb_reset(struct mb_interpreter_t** s, bool_t clrf) { + int result = MB_FUNC_OK; + _ht_node_t* global_scope = 0; + _ls_node_t* ast; + _parsing_context_t* context = 0; + _running_context_t* running = 0; + + mb_assert(s); + + (*s)->jump_set = _JMP_NIL; + (*s)->last_routine = 0; + (*s)->no_eat_comma_mark = 0; + (*s)->last_error = SE_NO_ERR; + (*s)->last_error_file = 0; + + running = (*s)->running_context; + _ls_clear((*s)->sub_stack); + (*s)->suspent_point = 0; + running->next_loop_var = 0; + memset(&(running->intermediate_value), 0, sizeof(mb_value_t)); + + (*s)->parsing_context = context = _reset_parsing_context((*s)->parsing_context); + + ast = (*s)->ast; + _ls_foreach(ast, _destroy_object); + _ls_clear(ast); + +#ifdef MB_ENABLE_STACK_TRACE + _ls_clear((*s)->stack_frames); +#endif /* MB_ENABLE_STACK_TRACE */ + + _clear_scope_chain(*s); + + if(clrf) { +#ifdef MB_ENABLE_MODULE + global_scope = (*s)->module_func_dict; + _ht_foreach(global_scope, _ht_destroy_module_func_list); + _ht_clear(global_scope); + _ls_foreach((*s)->using_modules, _destroy_memory); + _ls_clear((*s)->using_modules); +#endif /* MB_ENABLE_MODULE */ + + global_scope = (*s)->global_func_dict; + _ht_foreach(global_scope, _ls_free_extra); + _ht_clear(global_scope); + } + + result = _open_constant(*s); + mb_assert(MB_FUNC_OK == result); + + return result; +} + +/* Register an API function to a MY-BASIC environment */ +int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f) { + mb_assert(s && n && f); + + return _register_func(s, (char*)n, f, false); +} + +/* Remove an API function from a MY-BASIC environment */ +int mb_remove_func(struct mb_interpreter_t* s, const char* n) { + mb_assert(s && n); + + return _remove_func(s, (char*)n, false); +} + +/* Remove a reserved API from a MY-BASIC environment */ +int mb_remove_reserved_func(struct mb_interpreter_t* s, const char* n) { + mb_assert(s && n); + + return _remove_func(s, (char*)n, true); +} + +/* Begin a module, all functions registered within a module will put inside it */ +int mb_begin_module(struct mb_interpreter_t* s, const char* n) { + int result = MB_FUNC_OK; + + mb_assert(s && n); + +#ifdef MB_ENABLE_MODULE + if(s->with_module) { + _handle_error_on_obj(s, SE_RN_MODULE_NOT_MATCH, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); + } else { + s->with_module = mb_strdup(n, strlen(n) + 1); + } +#else /* MB_ENABLE_MODULE */ + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result); +#endif /* MB_ENABLE_MODULE */ + +_exit: + return result; +} + +/* End a module */ +int mb_end_module(struct mb_interpreter_t* s) { + int result = MB_FUNC_OK; + + mb_assert(s); + +#ifdef MB_ENABLE_MODULE + if(s->with_module) { + safe_free(s->with_module); + } else { + _handle_error_on_obj(s, SE_RN_MODULE_NOT_MATCH, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); + } + +#else /* MB_ENABLE_MODULE */ + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result); +#endif /* MB_ENABLE_MODULE */ + +_exit: + return result; +} + +/* Try attempting to begin an API function */ +int mb_attempt_func_begin(struct mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + obj = (_object_t*)ast->data; + if(!(obj->type == _DT_FUNC)) { + _handle_error_on_obj(s, SE_RN_INCOMPLETE_STRUCTURE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + + ++s->no_eat_comma_mark; + +_exit: + *l = ast; + + return result; +} + +/* Try attempting to end an API function */ +int mb_attempt_func_end(struct mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + --s->no_eat_comma_mark; + + return result; +} + +/* Try attempting an open bracket */ +int mb_attempt_open_bracket(struct mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + ast = ast->next; + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_open_bracket)) { + _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + +_exit: + *l = ast; + + return result; +} + +/* Try attempting a close bracket */ +int mb_attempt_close_bracket(struct mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + if(!ast) { + _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_close_bracket)) { + _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + +_exit: + *l = ast; + + return result; +} + +/* Detect if there is any more argument */ +int mb_has_arg(struct mb_interpreter_t* s, void** l) { + int result = 0; + _ls_node_t* ast = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + if(ast) { + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_close_bracket) && obj->type != _DT_EOS) + result = obj->type != _DT_SEP && obj->type != _DT_EOS; + } + + return result; +} + +/* Pop an integer argument */ +int mb_pop_int(struct mb_interpreter_t* s, void** l, int_t* val) { + int result = MB_FUNC_OK; + mb_value_t arg; + int_t tmp = 0; + + mb_assert(s && l && val); + + mb_make_nil(arg); + + mb_check(mb_pop_value(s, l, &arg)); + + switch(arg.type) { + case MB_DT_INT: + tmp = arg.value.integer; + + break; + case MB_DT_REAL: + tmp = (int_t)(arg.value.float_point); + + break; + default: + result = MB_FUNC_ERR; + + goto _exit; + } + + *val = tmp; + +_exit: + return result; +} + +/* Pop a float point argument */ +int mb_pop_real(struct mb_interpreter_t* s, void** l, real_t* val) { + int result = MB_FUNC_OK; + mb_value_t arg; + real_t tmp = 0; + + mb_assert(s && l && val); + + mb_make_nil(arg); + + mb_check(mb_pop_value(s, l, &arg)); + + switch(arg.type) { + case MB_DT_INT: + tmp = (real_t)(arg.value.integer); + + break; + case MB_DT_REAL: + tmp = arg.value.float_point; + + break; + default: + result = MB_FUNC_ERR; + + goto _exit; + } + + *val = tmp; + +_exit: + return result; +} + +/* Pop a string argument */ +int mb_pop_string(struct mb_interpreter_t* s, void** l, char** val) { + int result = MB_FUNC_OK; + mb_value_t arg; + char* tmp = 0; + + mb_assert(s && l && val); + + mb_make_nil(arg); + + mb_check(mb_pop_value(s, l, &arg)); + + switch(arg.type) { + case MB_DT_STRING: + tmp = arg.value.string; + + break; + default: + result = MB_FUNC_ERR; + + goto _exit; + } + + *val = tmp; + +_exit: + return result; +} + +/* Pop a usertype argument */ +int mb_pop_usertype(struct mb_interpreter_t* s, void** l, void** val) { + int result = MB_FUNC_OK; + mb_value_t arg; + void* tmp = 0; + + mb_assert(s && l && val); + + mb_make_nil(arg); + + mb_check(mb_pop_value(s, l, &arg)); + + switch(arg.type) { + case MB_DT_USERTYPE: + tmp = arg.value.usertype; + + break; + default: + result = MB_FUNC_ERR; + + goto _exit; + } + + *val = tmp; + +_exit: + return result; +} + +/* Pop an argument value */ +int mb_pop_value(struct mb_interpreter_t* s, void** l, mb_value_t* val) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t val_obj; + _object_t* val_ptr = 0; + _running_context_t* running = 0; + int* inep = 0; + + mb_assert(s && l && val); + + running = s->running_context; + + if(!_ls_empty(s->in_neg_expr)) + inep = (int*)_ls_back(s->in_neg_expr)->data; + + val_ptr = &val_obj; + _MAKE_NIL(val_ptr); + + ast = (_ls_node_t*)*l; + result = _calc_expression(s, &ast, &val_ptr); + if(result != MB_FUNC_OK) + goto _exit; + + if(val_ptr->type == _DT_STRING && !val_ptr->ref) { + _destroy_edge_objects(s); + _mark_edge_destroy_string(s, val_ptr->data.string); + } + _REF(val_ptr) + + if(s->no_eat_comma_mark < _NO_EAT_COMMA && (!inep || (inep && !(*inep)))) { + if(ast && _IS_SEP(ast->data, ',')) + ast = ast->next; + } + + result = _internal_object_to_public_value(val_ptr, val); + if(result != MB_FUNC_OK) + goto _exit; + +_exit: + *l = ast; + + return result; +} + +/* Push an integer argument */ +int mb_push_int(struct mb_interpreter_t* s, void** l, int_t val) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_int(arg, val); + mb_check(mb_push_value(s, l, arg)); + + return result; +} + +/* Push a float point argument */ +int mb_push_real(struct mb_interpreter_t* s, void** l, real_t val) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_real(arg, val); + mb_convert_to_int_if_posible(arg); + mb_check(mb_push_value(s, l, arg)); + + return result; +} + +/* Push a string argument */ +int mb_push_string(struct mb_interpreter_t* s, void** l, char* val) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_string(arg, val); + mb_check(mb_push_value(s, l, arg)); + _mark_lazy_destroy_string(s, val); + + return result; +} + +/* Push a usertype argument */ +int mb_push_usertype(struct mb_interpreter_t* s, void** l, void* val) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_usertype(arg, val); + mb_check(mb_push_value(s, l, arg)); + + return result; +} + +/* Push an argument value */ +int mb_push_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _object_t obj; + + mb_assert(s && l); + + running = s->running_context; + _assign_public_value(&running->intermediate_value, &val); + + _MAKE_NIL(&obj); + _public_value_to_internal_object(&running->intermediate_value, &obj); + _REF(&obj) + + _gc_try_trigger(s); + + return result; +} + +/* Begin a class */ +int mb_begin_class(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t** meta, int c, mb_value_t* out) { +#ifdef MB_ENABLE_CLASS + int result = MB_FUNC_OK; + _class_t* instance = 0; + _object_t* obj = 0; + _running_context_t* running = 0; + _ls_node_t* tmp = 0; + _var_t* var = 0; + int i = 0; + _object_t mo; + _class_t* mi = 0; + + mb_assert(s && l && n && out); + + running = s->running_context; + + tmp = (_ls_node_t*)*l; + + _using_jump_set_of_structured(s, tmp, _exit, result); + + tmp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); + if(tmp && tmp->data) { + obj = (_object_t*)tmp->data; + if(obj->type == _DT_VAR) + var = obj->data.variable; + } + if(s->last_instance || (obj && !var)) { + _handle_error_on_obj(s, SE_RN_DUPLICATE_CLASS, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + obj = _create_object(); + obj->type = _DT_CLASS; + + instance = (_class_t*)mb_malloc(sizeof(_class_t)); + _init_class(s, instance, mb_strdup(n, strlen(n) + 1)); + + for(i = 0; i < c; i++) { + if(meta[i]->type != MB_DT_CLASS) { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _MAKE_NIL(&mo); + _public_value_to_internal_object(meta[i], &mo); + mi = mo.data.instance; + _link_meta_class(s, instance, mi); + } + + _push_scope_by_class(s, instance->scope); + obj->data.instance = instance; + + if(var) { + _destroy_object(var->data, 0); + var->data = obj; + } else { + _ht_set_or_insert(running->var_dict, (void*)n, obj); + } + + s->last_instance = instance; + +_exit: + return result; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(n); + mb_unrefvar(meta); + mb_unrefvar(c); + mb_unrefvar(out); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_CLASS */ +} + +/* End a class */ +int mb_end_class(struct mb_interpreter_t* s, void** l) { +#ifdef MB_ENABLE_CLASS + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _pop_scope(s, false); + + s->last_instance = 0; + + return result; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(s); + mb_unrefvar(l); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_CLASS */ +} + +/* Get the userdata of a class instance */ +int mb_get_class_userdata(struct mb_interpreter_t* s, void** l, void** d) { +#ifdef MB_ENABLE_CLASS + int result = MB_FUNC_OK; + + mb_assert(s && l && d); + + if(s && s->last_instance) { + if(d) + *d = s->last_instance->userdata; + } else if(s && s->last_routine && s->last_routine->instance) { + if(d) + *d = s->last_routine->instance->userdata; + } else { + if(d) *d = 0; + + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +_exit: + return result; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(d); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_CLASS */ +} + +/* Set the userdata of a class instance */ +int mb_set_class_userdata(struct mb_interpreter_t* s, void** l, void* d) { +#ifdef MB_ENABLE_CLASS + int result = MB_FUNC_OK; + + mb_assert(s && l && d); + + if(s && s->last_instance) { + s->last_instance->userdata = d; + } else { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +_exit: + return result; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(d); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_CLASS */ +} + +/* Get a value by its identifier name */ +int mb_get_value_by_name(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) { + int result = MB_FUNC_OK; + _ls_node_t* tmp = 0; + _object_t* obj = 0; + + mb_assert(s && l && n); + + mb_make_nil(*val); + + tmp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NORMAL, 0, 0); + if(tmp && tmp->data) { + obj = (_object_t*)tmp->data; + _internal_object_to_public_value(obj, val); + } + + return result; +} + +/* Add a variable with a specific name */ +int mb_add_var(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t val, bool_t force) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _object_t* obj = 0; + _var_t* var = 0; + _ls_node_t* tmp = 0; + + mb_assert(s && l && n); + + running = s->running_context; + + tmp = _ht_find(running->var_dict, (void*)n); + + if(tmp) { + if(force) { + result = mb_set_var_value(s, tmp->data, val); + + goto _exit; + } else { + _handle_error_on_obj(s, SE_RN_DUPLICATE_ID, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + + var = _create_var(&obj, n, strlen(n) + 1, true); + _public_value_to_internal_object(&val, var->data); + + _ht_set_or_insert(running->var_dict, var->name, obj); + +_exit: + return result; +} + +/* Get a token literally, store it in an argument if it's a variable */ +int mb_get_var(struct mb_interpreter_t* s, void** l, void** v) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + + mb_assert(s && l); + + if(v) *v = 0; + + ast = (_ls_node_t*)*l; + if(ast) { + obj = (_object_t*)ast->data; + if(_IS_SEP(obj, ',')) { + ast = ast->next; + obj = (_object_t*)ast->data; + } + ast = ast->next; + } + + if(obj && obj->type == _DT_VAR) { + if(v) + *v = obj; + } + + if(ast && _IS_SEP(ast->data, ',')) + ast = ast->next; + + *l = ast; + + return result; +} + +/* Get the value of a variable */ +int mb_get_var_value(struct mb_interpreter_t* s, void* v, mb_value_t* val) { + int result = MB_FUNC_OK; + _object_t* obj = 0; + + mb_assert(s); + + if(!val || !v) + goto _exit; + + obj = (_object_t*)v; + if(obj->type != _DT_VAR) + goto _exit; + + _internal_object_to_public_value(obj->data.variable->data, val); + +_exit: + return result; +} + +/* Set the value of a variable */ +int mb_set_var_value(struct mb_interpreter_t* s, void* v, mb_value_t val) { + int result = MB_FUNC_OK; + _object_t* obj = 0; + + mb_assert(s); + + if(!v) + goto _exit; + obj = (_object_t*)v; + if(obj->type != _DT_VAR) + goto _exit; + + _public_value_to_internal_object(&val, obj->data.variable->data); + +_exit: + return result; +} + +/* Create an array */ +int mb_init_array(struct mb_interpreter_t* s, void** l, mb_data_e t, int* d, int c, void** a) { + int result = MB_FUNC_OK; + _array_t* arr = 0; + _data_e type = _DT_NIL; + int j = 0; + int n = 0; + mb_unrefvar(t); + + mb_assert(s && l && d && a); + + *a = 0; + if(c >= MB_MAX_DIMENSION_COUNT) { + _handle_error_on_obj(s, SE_RN_TOO_MANY_DIMENSIONS, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + for(j = 0; j < c; j++) { + n = d[j]; + if(n <= 0) { + _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + +#ifdef MB_SIMPLE_ARRAY + if(t == MB_DT_REAL) { + type = _DT_REAL; + } else if(t == MB_DT_STRING) { + type = _DT_STRING; + } else { + _handle_error_on_obj(s, SE_RN_NEED_COMPLEX_ARRAY, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } +#else /* MB_SIMPLE_ARRAY */ + type = _DT_REAL; +#endif /* MB_SIMPLE_ARRAY */ + + arr = _create_array(s, 0, type); + for(j = 0; j < c; j++) { + n = d[j]; + arr->dimensions[arr->dimension_count++] = n; + if(arr->count) + arr->count *= (unsigned)n; + else + arr->count += (unsigned)n; + } + _init_array(arr); + if(!arr->raw) { + arr->dimension_count = 0; + arr->dimensions[0] = 0; + arr->count = 0; + } + *a = arr; + +_exit: + return result; +} + +/* Get the length of an array */ +int mb_get_array_len(struct mb_interpreter_t* s, void** l, void* a, int r, int* i) { + int result = MB_FUNC_OK; + _array_t* arr = 0; + + mb_assert(s && l); + + arr = (_array_t*)a; + if(r < 0 || r >= arr->dimension_count) { + _handle_error_on_obj(s, SE_RN_RANK_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + if(i) + *i = arr->dimensions[r]; + +_exit: + return result; +} + +/* Get an element of an array with a specific index */ +int mb_get_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t* val) { + int result = MB_FUNC_OK; + _array_t* arr = 0; + int index = 0; + _data_e type = _DT_NIL; + + mb_assert(s && l); + + arr = (_array_t*)a; + if(c < 0 || c > arr->dimension_count) { + _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + if(!val) + goto _exit; + + index = _get_array_pos(s, arr, d, c); + if(index < 0) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + _get_array_elem(s, arr, index, &val->value, &type); + val->type = _internal_type_to_public_type(type); + +_exit: + return result; +} + +/* Set an element of an array with a specific index */ +int mb_set_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t val) { + int result = MB_FUNC_OK; + _array_t* arr = 0; + int index = 0; + _data_e type = _DT_NIL; + + mb_assert(s && l); + + arr = (_array_t*)a; + if(c < 0 || c > arr->dimension_count) { + _handle_error_on_obj(s, SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + index = _get_array_pos(s, arr, d, c); + if(index < 0) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + type = _public_type_to_internal_type(val.type); + _set_array_elem(s, 0, arr, (unsigned)index, &val.value, &type); + +_exit: + return result; +} + +/* Initialize a collection */ +int mb_init_coll(struct mb_interpreter_t* s, void** l, mb_value_t* coll) { + int result = MB_FUNC_OK; + + mb_assert(s); + +#ifdef MB_ENABLE_COLLECTION_LIB + switch(coll->type) { + case MB_DT_LIST: + coll->value.list = _create_list(s); + + break; + case MB_DT_DICT: + coll->value.dict = _create_dict(s); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(coll); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + return result; +} + +/* Get an element of a collection */ +int mb_get_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t* val) { + int result = MB_FUNC_OK; + _object_t ocoll; + int_t i = 0; + mb_value_t ret; + + mb_assert(s); + + mb_make_nil(ret); + + _MAKE_NIL(&ocoll); +#ifdef MB_ENABLE_COLLECTION_LIB + switch(coll.type) { + case MB_DT_LIST: + mb_int_val(idx, i); + _public_value_to_internal_object(&coll, &ocoll); + if(!_at_list(ocoll.data.list, i, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + if(!_find_dict(ocoll.data.dict, &idx, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(idx); + mb_unrefvar(coll); + mb_unrefvar(i); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + if(val) *val = ret; + + return result; +} + +/* Set an element of a collection */ +int mb_set_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t val) { + int result = MB_FUNC_OK; + _object_t ocoll; + int_t i = 0; + _object_t* oval = 0; + mb_value_t ret; + + mb_assert(s); + + mb_make_nil(ret); + + _MAKE_NIL(&ocoll); +#ifdef MB_ENABLE_COLLECTION_LIB + switch(coll.type) { + case MB_DT_LIST: + mb_int_val(idx, i); + _public_value_to_internal_object(&coll, &ocoll); + while((int)ocoll.data.list->count <= i) + _push_list(ocoll.data.list, &ret, 0); + if(!_set_list(ocoll.data.list, i, &val, &oval)) { + _destroy_object(oval, 0); + + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + _set_dict(ocoll.data.dict, &idx, &val, 0, 0); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(val); + mb_unrefvar(idx); + mb_unrefvar(coll); + mb_unrefvar(oval); + mb_unrefvar(i); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + return result; +} + +/* Remove an element from a collection */ +int mb_remove_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx) { + int result = MB_FUNC_OK; + _object_t ocoll; + int_t i = 0; + mb_value_t ret; + + mb_assert(s); + + mb_make_nil(ret); + + _MAKE_NIL(&ocoll); +#ifdef MB_ENABLE_COLLECTION_LIB + switch(coll.type) { + case MB_DT_LIST: + mb_int_val(idx, i); + _public_value_to_internal_object(&coll, &ocoll); + if(!_remove_at_list(ocoll.data.list, i)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + if(!_remove_dict(ocoll.data.dict, &idx)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(coll); + mb_unrefvar(idx); + mb_unrefvar(i); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + return result; +} + +/* Tell the element count of a collection */ +int mb_count_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, int* c) { + int result = MB_FUNC_OK; + _object_t ocoll; +#ifdef MB_ENABLE_COLLECTION_LIB + _list_t* lst = 0; + _dict_t* dct = 0; +#endif /* MB_ENABLE_COLLECTION_LIB */ + int ret = 0; + + mb_assert(s); + + _MAKE_NIL(&ocoll); +#ifdef MB_ENABLE_COLLECTION_LIB + switch(coll.type) { + case MB_DT_LIST: + lst = (_list_t*)coll.value.list; + ret = (int)lst->count; + + break; + case MB_DT_DICT: + dct = (_dict_t*)coll.value.dict; + ret = (int)_ht_count(dct->dict); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(coll); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + if(c) *c = ret; + + return result; +} + +/* Create a referenced usertype value */ +int mb_make_ref_value(struct mb_interpreter_t* s, void* val, mb_value_t* out, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs, mb_cmp_func_t cp, mb_fmt_func_t ft) { +#ifdef MB_ENABLE_USERTYPE_REF + int result = MB_FUNC_OK; + _usertype_ref_t* ref = 0; + + mb_assert(s && out); + + if(out) { + ref = _create_usertype_ref(s, val, un, cl, hs, cp, ft); + out->type = MB_DT_USERTYPE_REF; + out->value.usertype_ref = ref; + } + + return result; +#else /* MB_ENABLE_USERTYPE_REF */ + mb_unrefvar(s); + mb_unrefvar(val); + mb_unrefvar(out); + mb_unrefvar(un); + mb_unrefvar(cl); + mb_unrefvar(hs); + mb_unrefvar(cp); + mb_unrefvar(ft); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_USERTYPE_REF */ +} + +/* Get the data of a referenced usertype value */ +int mb_get_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val, void** out) { +#ifdef MB_ENABLE_USERTYPE_REF + int result = MB_FUNC_OK; + _usertype_ref_t* ref = 0; + + mb_assert(s && out); + + if(val.type != MB_DT_USERTYPE_REF) { + _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + if(out) { + ref = (_usertype_ref_t*)val.value.usertype_ref; + *out = ref->usertype; + } + +_exit: + return result; +#else /* MB_ENABLE_USERTYPE_REF */ + mb_unrefvar(s); + mb_unrefvar(l); + mb_unrefvar(val); + mb_unrefvar(out); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_USERTYPE_REF */ +} + +/* Increase the reference of a value by 1 */ +int mb_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { + int result = MB_FUNC_OK; + _object_t obj; + + mb_assert(s && l); + + _MAKE_NIL(&obj); + _public_value_to_internal_object(&val, &obj); + if( +#ifdef MB_ENABLE_USERTYPE_REF + obj.type != _DT_USERTYPE_REF && +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_COLLECTION_LIB + obj.type != _DT_LIST && obj.type != _DT_DICT && +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + obj.type != _DT_CLASS && +#endif /* MB_ENABLE_CLASS */ + obj.type != _DT_ARRAY + ) { + _handle_error_on_obj(s, SE_RN_REFERENCED_TYPE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _REF(&obj); + +_exit: + return result; +} + +/* Decrease the reference of a value by 1 */ +int mb_unref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) { + int result = MB_FUNC_OK; + _object_t obj; + + mb_assert(s && l); + + _MAKE_NIL(&obj); + _public_value_to_internal_object(&val, &obj); + if( +#ifdef MB_ENABLE_USERTYPE_REF + obj.type != _DT_USERTYPE_REF && +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifdef MB_ENABLE_COLLECTION_LIB + obj.type != _DT_LIST && obj.type != _DT_DICT && +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + obj.type != _DT_CLASS && +#endif /* MB_ENABLE_CLASS */ + obj.type != _DT_ARRAY + ) { + _handle_error_on_obj(s, SE_RN_REFERENCED_TYPE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _UNREF(&obj); + +_exit: + return result; +} + +/* Override a meta function of a value */ +int mb_override_value(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_meta_func_u m, void* f) { + int result = MB_FUNC_OK; + _object_t obj; + + mb_assert(s && l); + +#ifdef MB_ENABLE_USERTYPE_REF + _MAKE_NIL(&obj); + if(val.type == MB_DT_USERTYPE_REF) { + _usertype_ref_t* user = 0; + _public_value_to_internal_object(&val, &obj); + user = obj.data.usertype_ref; + if(m & MB_MF_CALC) { + if(!user->calc_operators) { + user->calc_operators = (_calculation_operator_info_t*)mb_malloc(sizeof(_calculation_operator_info_t)); + memset(user->calc_operators, 0, sizeof(_calculation_operator_info_t)); + } + } + switch(m) { + case MB_MF_IS: + user->calc_operators->is = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_ADD: + user->calc_operators->add = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_SUB: + user->calc_operators->sub = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_MUL: + user->calc_operators->mul = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_DIV: + user->calc_operators->div = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_NEG: + user->calc_operators->neg = (mb_meta_operator_t)(intptr_t)f; + + break; + case MB_MF_COLL: + user->coll_func = (mb_meta_func_t)(intptr_t)f; + + break; + case MB_MF_FUNC: + user->generic_func = (mb_meta_func_t)(intptr_t)f; + + break; + default: /* Do nothing */ + break; + } + } else { + result = MB_FUNC_ERR; + } +#else /* MB_ENABLE_USERTYPE_REF */ + mb_unrefvar(obj); + mb_unrefvar(val); + mb_unrefvar(m); + mb_unrefvar(f); +#endif /* MB_ENABLE_USERTYPE_REF */ + + return result; +} + +/* Dispose a value */ +int mb_dispose_value(struct mb_interpreter_t* s, mb_value_t val) { + int result = MB_FUNC_OK; + + mb_assert(s); + + if(val.type == MB_DT_STRING) + safe_free(val.value.string); + + _assign_public_value(&val, 0); + + return result; +} + +/* Get a sub routine with a specific name */ +int mb_get_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) { + int result = MB_FUNC_OK; + _object_t* obj = 0; + _ls_node_t* scp = 0; + + mb_assert(s && l && n && val); + + mb_make_nil(*val); + + scp = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); + if(scp) { + obj = (_object_t*)scp->data; + if(obj) { + if(obj->type == _DT_ROUTINE) { + _internal_object_to_public_value(obj, val); + } else { + _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + } + +_exit: + return result; +} + +/* Set a sub routine with a specific name and native function pointer */ +int mb_set_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_routine_func_t f, bool_t force) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _object_t* obj = 0; + _routine_t* routine = 0; + _ls_node_t* tmp = 0; + _var_t* var = 0; + + mb_assert(s && l); + + running = s->running_context; + + tmp = _ht_find(running->var_dict, (void*)n); + + if(tmp) { + if(force) { + obj = (_object_t*)tmp->data; + if(obj->type == _DT_VAR) + var = obj->data.variable; + } else { + _handle_error_on_obj(s, SE_RN_DUPLICATE_ROUTINE, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + + routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); + _init_routine(s, routine, mb_strdup(n, strlen(n) + 1), f); + + obj = _create_object(); + obj->type = _DT_ROUTINE; + obj->data.routine = routine; + obj->ref = false; + +#ifdef MB_ENABLE_CLASS + routine->instance = s->last_instance; +#endif /* MB_ENABLE_CLASS */ + + if(var && force) { + _destroy_object(var->data, 0); + var->data = obj; + } else { + _ht_set_or_insert(running->var_dict, routine->name, obj); + } + +_exit: + return result; +} + +/* Evaluate a sub routine */ +int mb_eval_routine(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_value_t* args, unsigned argc) { + int result = MB_FUNC_OK; + _object_t obj; + + if(val.type != MB_DT_ROUTINE) { + _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + _MAKE_NIL(&obj); + _public_value_to_internal_object(&val, &obj); + result = _eval_routine(s, (_ls_node_t**)l, args, argc, obj.data.routine, _has_routine_fun_arg, _pop_routine_fun_arg); + +_exit: + return result; +} + +/* Load and parse a script string */ +int mb_load_string(struct mb_interpreter_t* s, const char* l, bool_t reset) { + int result = MB_FUNC_OK; + int status = 0; + unsigned short _row = 0; + unsigned short _col = 0; + char wrapped = _ZERO_CHAR; + _parsing_context_t* context = 0; + + mb_assert(s); + + if(!s->parsing_context) + s->parsing_context = _reset_parsing_context(s->parsing_context); + + context = s->parsing_context; + + mb_uu_getbom(&l); + while(*l) { + int n = 1; +#ifdef MB_ENABLE_UNICODE_ID + if(context->parsing_state == _PS_NORMAL) + n = mb_uu_ischar(l); +#endif /* MB_ENABLE_UNICODE_ID */ + do { + if(n == 1) { + char ch = *l; + if((ch == _NEWLINE_CHAR || ch == _RETURN_CHAR) && (!wrapped || wrapped == ch)) { + unsigned short before = 0; + wrapped = ch; + before = context->parsing_row++; + context->parsing_col = 0; + if(before > context->parsing_row) { + context->parsing_col = 1; + _handle_error_now(s, SE_RN_PROGRAM_TOO_LONG, s->last_error_file, MB_FUNC_ERR); + + goto _exit; + } + + break; + } + } + wrapped = _ZERO_CHAR; + ++context->parsing_col; + } while(0); + status = _parse_char(s, l, n, context->parsing_pos, _row, _col); + result = status; + if(status) { + _set_error_pos(s, context->parsing_pos, _row, _col); + _handle_error_now(s, s->last_error, s->last_error_file, result); + + goto _exit; + } + _row = context->parsing_row; + _col = context->parsing_col; + ++context->parsing_pos; + l += n; + }; + status = _parse_char(s, 0, 1, context->parsing_pos, context->parsing_row, context->parsing_col); + +_exit: + if(reset) + _end_of_file(context); + + return result; +} + +/* Load and parse a script file */ +int mb_load_file(struct mb_interpreter_t* s, const char* f) { + int result = MB_FUNC_OK; + char* buf = 0; + _parsing_context_t* context = 0; + + mb_assert(s); + + context = s->parsing_context; + + s->parsing_context = context = _reset_parsing_context(s->parsing_context); + + buf = _load_file(s, f, 0); + if(buf) { + result = mb_load_string(s, buf, true); + safe_free(buf); + + if(result) + goto _exit; + } else { + _set_current_error(s, SE_PS_OPEN_FILE_FAILED, 0); + + result = MB_FUNC_ERR; + } + +_exit: + context->parsing_state = _PS_NORMAL; + + return result; +} + +/* Run the current AST */ +int mb_run(struct mb_interpreter_t* s) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + + mb_assert(s); + + if(s->parsing_context) { + if(s->parsing_context->routine_state) { + result = MB_FUNC_ERR; + _handle_error_now(s, SE_RN_INCOMPLETE_ROUTINE, s->source_file, result); + + goto _exit; + } +#ifdef MB_ENABLE_CLASS + if(s->parsing_context->class_state != _CLASS_STATE_NONE) { + result = MB_FUNC_ERR; + _handle_error_now(s, SE_RN_INCOMPLETE_CLASS, s->source_file, result); + + goto _exit; + } +#endif /* MB_ENABLE_CLASS */ + } + + _destroy_parsing_context(&s->parsing_context); + + s->handled_error = false; + + if(s->suspent_point) { + ast = s->suspent_point; + ast = ast->next; + s->suspent_point = 0; + } else { + s->source_file = 0; +#ifdef MB_ENABLE_CLASS + s->last_instance = 0; + s->calling = false; +#endif /* MB_ENABLE_CLASS */ + s->last_routine = 0; + + mb_assert(!s->no_eat_comma_mark); + while(s->running_context->prev) + s->running_context = s->running_context->prev; + ast = s->ast; + ast = ast->next; + if(!ast) { + result = MB_FUNC_ERR; + _set_error_pos(s, 0, 0, 0); + _handle_error_now(s, SE_RN_EMPTY_PROGRAM, s->source_file, result); + + goto _exit; + } + } + + do { + result = _execute_statement(s, &ast, true); + if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { + if(result != MB_FUNC_SUSPEND && s->error_handler) { + if(result >= MB_EXTENDED_ABORT) + s->last_error = SE_EA_EXTENDED_ABORT; + _handle_error_now(s, s->last_error, s->last_error_file, result); + } + + goto _exit; + } + } while(ast); + +_exit: + _destroy_edge_objects(s); + + return result; +} + +/* Suspend current execution and save the context */ +int mb_suspend(struct mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + + mb_assert(s && l && *l); + + ast = (_ls_node_t*)*l; + s->suspent_point = ast; + + return result; +} + +/* Schedule to suspend current execution */ +int mb_schedule_suspend(struct mb_interpreter_t* s, int t) { + int result = MB_FUNC_OK; + + mb_assert(s); + + if(t == MB_FUNC_OK) + t = MB_FUNC_SUSPEND; + s->schedule_suspend_tag = t; + + return result; +} + +/* Get the value of an identifier */ +int mb_debug_get(struct mb_interpreter_t* s, const char* n, mb_value_t* val) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _ls_node_t* v = 0; + _object_t* obj = 0; + mb_value_t tmp; + + mb_assert(s && n); + + running = s->running_context; + + v = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); + if(v) { + obj = (_object_t*)v->data; + mb_assert(obj->type == _DT_VAR); + if(val) + result = _internal_object_to_public_value(obj->data.variable->data, val); + else + result = _internal_object_to_public_value(obj->data.variable->data, &tmp); + } else { + if(val) { + mb_make_nil(*val); + } + _handle_error_on_obj(s, SE_RN_DEBUG_ID_NOT_FOUND, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); + } + +_exit: + return result; +} + +/* Set the value of an identifier */ +int mb_debug_set(struct mb_interpreter_t* s, const char* n, mb_value_t val) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _ls_node_t* v = 0; + _object_t* obj = 0; + + mb_assert(s && n); + + running = s->running_context; + + v = _search_identifier_in_scope_chain(s, 0, n, _PATHING_NONE, 0, 0); + if(v) { + obj = (_object_t*)v->data; + mb_assert(obj->type == _DT_VAR); + result = _public_value_to_internal_object(&val, obj->data.variable->data); + } else { + _handle_error_on_obj(s, SE_RN_DEBUG_ID_NOT_FOUND, s->source_file, (_object_t*)0, MB_FUNC_ERR, _exit, result); + } + +_exit: + return result; +} + +/* Get stack frame names of a MY-BASIC environment */ +int mb_debug_get_stack_trace(struct mb_interpreter_t* s, void** l, char** fs, unsigned fc) { +#ifdef MB_ENABLE_STACK_TRACE + int result = MB_FUNC_OK; + _ls_node_t* f = 0; + unsigned i = 0; + mb_unrefvar(l); + + mb_assert(s); + + if(fs && fc) { + if(_ls_count(s->stack_frames) > fc) + fs[--fc] = "..."; + f = s->stack_frames->prev; + while(f != s->stack_frames && f && f->data && i < fc) { + fs[i++] = (char*)f->data; + f = f->prev; + } + } + while(i < fc) + fs[i++] = 0; + + return result; +#else /* MB_ENABLE_STACK_TRACE */ + int result = MB_FUNC_OK; + mb_unrefvar(fs); + mb_unrefvar(fc); + + _handle_error_on_obj(s, SE_RN_STACK_TRACE_DISABLED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + +_exit: + return result; +#endif /* MB_ENABLE_STACK_TRACE */ +} + +/* Set a stepped handler to a MY-BASIC environment */ +int mb_debug_set_stepped_handler(struct mb_interpreter_t* s, mb_debug_stepped_handler_t h) { + int result = MB_FUNC_OK; + + mb_assert(s); + + s->debug_stepped_handler = h; + + return result; +} + +/* Get type description text */ +const char* mb_get_type_string(mb_data_e t) { + switch(t) { + case MB_DT_NIL: + return "NIL"; + case MB_DT_UNKNOWN: + return "UNKNOWN"; + case MB_DT_INT: + return "INTEGER"; + case MB_DT_REAL: + return "REAL"; + case MB_DT_NUM: + return "NUMBER"; + case MB_DT_STRING: + return "STRING"; + case MB_DT_TYPE: + return "TYPE"; + case MB_DT_USERTYPE: + return "USERTYPE"; +#ifdef MB_ENABLE_USERTYPE_REF + case MB_DT_USERTYPE_REF: + return "USERTYPE_REF"; +#endif /* MB_ENABLE_USERTYPE_REF */ + case MB_DT_ARRAY: + return "ARRAY"; +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + return "LIST"; + case MB_DT_LIST_IT: + return "LIST_ITERATOR"; + case MB_DT_DICT: + return "DICT"; + case MB_DT_DICT_IT: + return "DICT_ITERATOR"; + case MB_DT_COLLECTION: + return "COLLECTION"; + case MB_DT_ITERATOR: + return "ITERATOR"; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + return "CLASS"; +#endif /* MB_ENABLE_CLASS */ + case MB_DT_ROUTINE: + return "ROUTINE"; + default: /* Return a not exist string */ + return ""; + } +} + +/* Raise an error */ +int mb_raise_error(struct mb_interpreter_t* s, void** l, mb_error_e err, int ret) { + int result = MB_FUNC_ERR; + + mb_assert(s); + + _handle_error_on_obj(s, err, s->source_file, DON2(l), ret, _exit, result); + +_exit: + return result; +} + +/* Get the last error information */ +mb_error_e mb_get_last_error(struct mb_interpreter_t* s) { + mb_error_e result = SE_NO_ERR; + + mb_assert(s); + + result = s->last_error; + s->last_error = SE_NO_ERR; + + return result; +} + +/* Get the error description text */ +const char* mb_get_error_desc(mb_error_e err) { + if(err < countof(_ERR_DESC)) + return _ERR_DESC[err]; + + return ""; +} + +/* Set an error handler to a MY-BASIC environment */ +int mb_set_error_handler(struct mb_interpreter_t* s, mb_error_handler_t h) { + int result = MB_FUNC_OK; + + mb_assert(s); + + s->error_handler = h; + + return result; +} + +/* Set a print functor to a MY-BASIC environment */ +int mb_set_printer(struct mb_interpreter_t* s, mb_print_func_t p) { + int result = MB_FUNC_OK; + + mb_assert(s); + + s->printer = p; + + return result; +} + +/* Set an input functor to a MY-BASIC environment */ +int mb_set_inputer(struct mb_interpreter_t* s, mb_input_func_t p) { + int result = MB_FUNC_OK; + + mb_assert(s); + + s->inputer = p; + + return result; +} + +/* Trigger GC */ +int mb_gc(struct mb_interpreter_t* s, int_t* collected) { + int_t diff = 0; + + mb_assert(s); + + diff = (int_t)_mb_allocated; + _gc_collect_garbage(s, 1); + diff = (int_t)(_mb_allocated - diff); + if(collected) + *collected = diff; + + return MB_FUNC_OK; +} + +/* Get the userdata of a MY-BASIC environment */ +int mb_get_userdata(struct mb_interpreter_t* s, void** d) { + int result = MB_FUNC_OK; + + mb_assert(s && d); + + if(s && d) + *d = s->userdata; + + return result; +} + +/* Set the userdata of a MY-BASIC environment */ +int mb_set_userdata(struct mb_interpreter_t* s, void* d) { + int result = MB_FUNC_OK; + + mb_assert(s); + + if(s) + s->userdata = d; + + return result; +} + +/* Set an import handler to a MY-BASIC environment */ +int mb_set_import_handler(struct mb_interpreter_t* s, mb_import_handler_t h) { + int result = MB_FUNC_OK; + + mb_assert(s); + + s->import_handler = h; + + return result; +} + +/* Safe stdin reader function */ +int mb_gets(char* buf, int s) { + int result = 0; + + if(fgets(buf, s, stdin) == 0) { + fprintf(stderr, "Error reading.\n"); + + exit(1); + } + result = (int)strlen(buf); + if(buf[result - 1] == _NEWLINE_CHAR) { + buf[result - 1] = _ZERO_CHAR; + result--; + } + + return result; +} + +/* Duplicate a string for internal use */ +char* mb_memdup(const char* val, unsigned size) { + char* result = 0; + + if(val != 0) { + result = (char*)mb_malloc(size); + memcpy(result, val, size); + } + + return result; +} + +/* Register an allocator and a freer globally */ +int mb_set_memory_manager(mb_memory_allocate_func_t a, mb_memory_free_func_t f) { + _mb_allocate_func = a; + _mb_free_func = f; + + return MB_FUNC_OK; +} + +/* ========================================================} */ + +/* +** {======================================================== +** Lib definitions +*/ + +/** Core lib */ + +/* Operator #, dummy assignment */ +static int _core_dummy_assign(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* Operator + */ +static int _core_add(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_obj_meta_obj(add, l, result, _exit); + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_connect_strings(l); + } else { + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); + } + } else { + _instruct_num_op_num(+, l); + } + +_exit: + return result; +} + +/* Operator - */ +static int _core_min(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_obj_meta_obj(sub, l, result, _exit); + _instruct_num_op_num(-, l); + + goto _exit; /* Avoid an unreferenced label warning */ + +_exit: + return result; +} + +/* Operator * */ +static int _core_mul(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_obj_meta_obj(mul, l, result, _exit); + _instruct_num_op_num(*, l); + + goto _exit; /* Avoid an unreferenced label warning */ + +_exit: + return result; +} + +/* Operator / */ +static int _core_div(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_obj_meta_obj(div, l, result, _exit); + _proc_div_by_zero(s, l, _exit, result, SE_RN_DIVIDE_BY_ZERO); + _instruct_num_op_num(/, l); + +_exit: + return result; +} + +/* Operator MOD */ +static int _core_mod(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _proc_div_by_zero(s, l, _exit, result, SE_RN_MOD_BY_ZERO); + _instruct_int_op_int(%, l); + +_exit: + return result; +} + +/* Operator ^ */ +static int _core_pow(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_fun_num_num(pow, l); + + return result; +} + +/* Operator ( */ +static int _core_open_bracket(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* Operator ) */ +static int _core_close_bracket(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + +#ifdef MB_ENABLE_LAMBDA + if(s->last_routine && s->last_routine->type == _IT_LAMBDA) { + result = _core_return(s, l); + + goto _exit; + } +#endif /* MB_ENABLE_LAMBDA */ + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* Operator - (negative) */ +static int _core_neg(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + mb_value_t arg; + _running_context_t* running = 0; + int* inep = 0; + int calc_depth = 0; +#ifdef MB_ENABLE_USERTYPE_REF + _object_t obj; +#endif /* MB_ENABLE_USERTYPE_REF */ + + mb_assert(s && l); + + running = s->running_context; + ast = (_ls_node_t*)*l; + if(ast) ast = ast->next; + + if(!_ls_empty(s->in_neg_expr)) + inep = (int*)_ls_back(s->in_neg_expr)->data; + + if(inep) + (*inep)++; + + calc_depth = running->calc_depth; + + mb_make_nil(arg); + if(ast && _IS_FUNC((_object_t*)ast->data, _core_open_bracket)) { + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + } else { + running->calc_depth = 1; + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_func_end(s, l)); + } + + if(inep) + (*inep)--; + + switch(arg.type) { + case MB_DT_INT: + arg.value.integer = -(arg.value.integer); + + break; + case MB_DT_REAL: + arg.value.float_point = -(arg.value.float_point); + + break; +#ifdef MB_ENABLE_USERTYPE_REF + case MB_DT_USERTYPE_REF: + _MAKE_NIL(&obj); + _public_value_to_internal_object(&arg, &obj); + if(obj.data.usertype_ref->calc_operators && obj.data.usertype_ref->calc_operators->neg) { + mb_meta_operator_t neg = obj.data.usertype_ref->calc_operators->neg; + mb_check(mb_ref_value(s, l, arg)); + mb_check(mb_unref_value(s, l, arg)); + mb_check(neg(s, l, &arg, 0, &arg)); + + break; + } + /* Fall through */ +#endif /* MB_ENABLE_USERTYPE_REF */ + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, arg)); + +_exit: + running->calc_depth = calc_depth; + + return result; +} + +/* Operator = (equal) */ +static int _core_equal(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(==, l); + } else { + _set_tuple3_result(l, 0); + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(==, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(==, l); + } + +_exit: + return result; +} + +/* Operator < */ +static int _core_less(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(<, l); + } else { + if(_is_string(((_tuple3_t*)*l)->e1)) { + _set_tuple3_result(l, 0); + } else { + _set_tuple3_result(l, 1); + } + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(<, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(<, l); + } + +_exit: + return result; +} + +/* Operator > */ +static int _core_greater(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(>, l); + } else { + if(_is_string(((_tuple3_t*)*l)->e1)) { + _set_tuple3_result(l, 1); + } else { + _set_tuple3_result(l, 0); + } + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(>, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(>, l); + } + +_exit: + return result; +} + +/* Operator <= */ +static int _core_less_equal(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(<=, l); + } else { + if(_is_string(((_tuple3_t*)*l)->e1)) { + _set_tuple3_result(l, 0); + } else { + _set_tuple3_result(l, 1); + } + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(<=, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(<=, l); + } + +_exit: + return result; +} + +/* Operator >= */ +static int _core_greater_equal(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(>=, l); + } else { + if(_is_string(((_tuple3_t*)*l)->e1)) { + _set_tuple3_result(l, 1); + } else { + _set_tuple3_result(l, 0); + } + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(>=, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(>=, l); + } + +_exit: + return result; +} + +/* Operator <> */ +static int _core_not_equal(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _tuple3_t* tpr = 0; + + mb_assert(s && l); + + if(_is_string(((_tuple3_t*)*l)->e1) || _is_string(((_tuple3_t*)*l)->e2)) { + if(_is_string(((_tuple3_t*)*l)->e1) && _is_string(((_tuple3_t*)*l)->e2)) { + _instruct_compare_strings(!=, l); + } else { + _set_tuple3_result(l, 1); + _handle_error_on_obj(s, SE_RN_STRING_EXPECTED, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result); + } + } else if(_is_number(((_tuple3_t*)*l)->e1) && _is_number(((_tuple3_t*)*l)->e2)) { + _instruct_num_op_num(!=, l); + tpr = (_tuple3_t*)*l; + if(((_object_t*)tpr->e3)->type != _DT_INT) { + ((_object_t*)tpr->e3)->type = _DT_INT; + ((_object_t*)tpr->e3)->data.integer = ((_object_t*)tpr->e3)->data.float_point != 0.0f; + } + } else { + _instruct_obj_op_obj(!=, l); + } + +_exit: + return result; +} + +/* Operator AND */ +static int _core_and(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_bool_op_bool(&&, l); + + return result; +} + +/* Operator OR */ +static int _core_or(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + _instruct_bool_op_bool(||, l); + + return result; +} + +/* Operator NOT */ +static int _core_not(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + mb_value_t arg; + _running_context_t* running = 0; + int calc_depth = 0; + + mb_assert(s && l); + + running = s->running_context; + ast = (_ls_node_t*)*l; + if(ast) ast = ast->next; + + calc_depth = running->calc_depth; + + mb_make_nil(arg); + if(ast && _IS_FUNC((_object_t*)ast->data, _core_open_bracket)) { + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + } else { + running->calc_depth = 1; + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_func_end(s, l)); + } + + switch(arg.type) { + case MB_DT_NIL: + mb_make_bool(arg, true); + + break; + case MB_DT_INT: + mb_make_bool(arg, !arg.value.integer); + + break; + case MB_DT_REAL: + mb_make_bool(arg, arg.value.float_point == (real_t)0); + + break; + default: + mb_make_bool(arg, false); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + + running->calc_depth = calc_depth; + + return result; +} + +/* Operator IS */ +static int _core_is(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _object_t* fst = 0; + _object_t* scd = 0; + _object_t* val = 0; + bool_t is_a = 0; + + mb_assert(s && l); + + _instruct_obj_meta_obj(is, l, result, _exit); + + fst = (_object_t*)((_tuple3_t*)*l)->e1; + scd = (_object_t*)((_tuple3_t*)*l)->e2; + val = (_object_t*)((_tuple3_t*)*l)->e3; + + if(fst && fst->type == _DT_VAR) fst = fst->data.variable->data; + if(scd && scd->type == _DT_VAR) scd = scd->data.variable->data; + if(!fst || !scd) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); + } + if(scd->type == _DT_TYPE) { + val->type = _DT_INT; + val->data.integer = (int_t)(!!(_internal_type_to_public_type(fst->type) & scd->data.type)); + } else { +#ifdef MB_ENABLE_CLASS + if(!_IS_CLASS(fst) || !_IS_CLASS(scd)) { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); + } + _traverse_class(fst->data.instance, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, scd->data.instance, &is_a); + val->type = _DT_INT; + val->data.integer = (int_t)is_a; +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(is_a); + + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); +#endif /* MB_ENABLE_CLASS */ + } + +_exit: + return result; +} + +/* LET statement */ +static int _core_let(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _running_context_t* running = 0; + _var_t* var = 0; + _var_t* evar = 0; + int refc = 1; + _array_t* arr = 0; + _object_t* arr_obj = 0; + unsigned arr_idx = 0; + bool_t literally = false; + _object_t* val = 0; + bool_t freed = false; +#ifdef MB_ENABLE_COLLECTION_LIB + int_t idx = 0; + mb_value_t key; + bool_t is_coll = false; +#endif /* MB_ENABLE_COLLECTION_LIB */ + + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)*l; + obj = (_object_t*)ast->data; + if(obj->type == _DT_FUNC) + ast = ast->next; + if(!ast || !ast->data) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + obj = (_object_t*)ast->data; +#ifdef MB_ENABLE_CLASS + if(_IS_VAR(obj)) { + _ls_node_t* fn = 0; + if(_is_valid_class_accessor_following_routine(s, obj->data.variable, ast, &fn)) { + if(fn) + obj = (_object_t*)fn->data; + } + } +#endif /* MB_ENABLE_CLASS */ + if(obj->type == _DT_ARRAY) { + arr_obj = obj; + arr = _search_array_in_scope_chain(s, obj->data.array, &arr_obj); + result = _get_array_index(s, &ast, 0, &arr_idx, &literally); + if(result != MB_FUNC_OK) + goto _exit; + } else if(obj->type == _DT_VAR && obj->data.variable->data->type == _DT_ARRAY) { + arr_obj = obj->data.variable->data; + arr = _search_array_in_scope_chain(s, obj->data.variable->data->data.array, &arr_obj); + result = _get_array_index(s, &ast, 0, &arr_idx, &literally); + if(result != MB_FUNC_OK) + goto _exit; + } else if(obj->type == _DT_VAR) { + if(_IS_ME(obj->data.variable)) { + _handle_error_on_obj(s, SE_RN_CANNOT_CHANGE_ME, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } else { + evar = obj->data.variable; + var = _search_var_in_scope_chain(s, obj->data.variable); + if(var == evar) evar = 0; + } + } else { + _handle_error_on_obj(s, SE_RN_VAR_OR_ARRAY_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + ast = ast->next; + if(!ast || !ast->data) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } +#ifdef MB_ENABLE_COLLECTION_LIB + if(var && _IS_COLL(var->data)) { + obj = (_object_t*)ast->data; + if(_IS_FUNC(obj, _core_open_bracket)) { + mb_check(mb_attempt_open_bracket(s, l)); + + switch(var->data->type) { + case _DT_LIST: + mb_check(mb_pop_int(s, l, &idx)); + + break; + case _DT_DICT: + mb_make_nil(key); + mb_check(mb_pop_value(s, l, &key)); + + break; + default: /* Do nothing */ + break; + } + + mb_check(mb_attempt_close_bracket(s, l)); + + ast = (_ls_node_t*)*l; + is_coll = true; + } + } +#endif /* MB_ENABLE_COLLECTION_LIB */ + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_equal)) { /* Is it an assign operator? */ + _handle_error_on_obj(s, SE_RN_ASSIGN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + ast = ast->next; + val = _create_object(); + result = _calc_expression(s, &ast, &val); + + if(var) { +#ifdef MB_ENABLE_COLLECTION_LIB + if(is_coll) { + switch(var->data->type) { + case _DT_LIST: + if(!_set_list(var->data->data.list, idx, 0, &val)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case _DT_DICT: + _set_dict(var->data->data.dict, &key, 0, 0, val); + + break; + default: /* Do nothing */ + break; + } + + goto _exit; + } +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + if(evar && evar->pathing && evar->data->type == _DT_STRING && !(var->data->type == _DT_STRING && evar->data->data.string != var->data->data.string)) { + _dispose_object(evar->data); + evar = 0; + freed = true; + } +_proc_extra_var: +#endif /* MB_ENABLE_CLASS */ + if(!freed) + _dispose_object(var->data); + var->data->type = val->type; +#ifdef MB_ENABLE_COLLECTION_LIB + if(val->type == _DT_LIST_IT || val->type == _DT_DICT_IT) + _assign_with_it(var->data, val); + else + var->data->data = val->data; +#else /* MB_ENABLE_COLLECTION_LIB */ + var->data->data = val->data; +#endif /* MB_ENABLE_COLLECTION_LIB */ + if(val->type == _DT_ROUTINE) { +#ifdef MB_ENABLE_LAMBDA + if(val->data.routine->type == _IT_LAMBDA) + var->data->ref = val->ref; + else + var->data->ref = 1; +#else /* MB_ENABLE_LAMBDA */ + var->data->ref = 1; +#endif /* MB_ENABLE_LAMBDA */ + } else { + var->data->ref = val->ref; + } +#ifdef MB_ENABLE_CLASS + if(evar && evar->pathing) { + var = evar; + evar = 0; + refc++; + + goto _proc_extra_var; + } +#endif /* MB_ENABLE_CLASS */ + } else if(arr && literally) { + if(val->type != _DT_UNKNOWN) { +#ifdef MB_ENABLE_ARRAY_REF + _unref(&arr_obj->data.array->ref, arr_obj->data.array); +#endif /* MB_ENABLE_ARRAY_REF */ + arr_obj->type = val->type; +#ifdef MB_ENABLE_COLLECTION_LIB + if(val->type == _DT_LIST_IT || val->type == _DT_DICT_IT) + _assign_with_it(arr_obj, val); + else + arr_obj->data = val->data; +#else /* MB_ENABLE_COLLECTION_LIB */ + arr_obj->data = val->data; +#endif /* MB_ENABLE_COLLECTION_LIB */ + arr_obj->ref = val->ref; + } + } else if(arr) { + mb_value_u _val; + switch(val->type) { + case _DT_NIL: /* Fall through */ + case _DT_INT: /* Fall through */ + case _DT_REAL: /* Fall through */ + case _DT_STRING: /* Fall through */ + case _DT_USERTYPE: + _COPY_BYTES(_val.bytes, val->data.bytes); + + break; + default: + safe_free(val); + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + result = _set_array_elem(s, ast, arr, arr_idx, &_val, &val->type); + if(result != MB_FUNC_OK) + goto _exit; + if(val->type == _DT_STRING && !val->ref) { + safe_free(val->data.string); + } + } + while(refc--) { + _REF(val) + } + safe_free(val); + +_exit: + *l = ast; + + return result; +} + +/* DIM statement */ +static int _core_dim(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* arr = 0; + _object_t* len = 0; + mb_value_u val; + _array_t dummy; + + mb_assert(s && l); + + /* Array name */ + ast = (_ls_node_t*)*l; + if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_ARRAY) { + _handle_error_on_obj(s, SE_RN_ARRAY_IDENTIFIER_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + arr = (_object_t*)ast->data; + memset(&dummy, 0, sizeof(_array_t)); + dummy.type = arr->data.array->type; + dummy.name = arr->data.array->name; + /* ( */ + if(!ast->next || ((_object_t*)ast->next->data)->type != _DT_FUNC || ((_object_t*)ast->next->data)->data.func->pointer != _core_open_bracket) { + _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + /* Array subscript */ + if(!ast->next) { + _handle_error_on_obj(s, SE_RN_ARRAY_SUBSCRIPT_EXPECTED, s->source_file, (ast && ast->next) ? ((_object_t*)ast->next->data) : 0, MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + while(((_object_t*)ast->data)->type != _DT_FUNC || ((_object_t*)ast->data)->data.func->pointer != _core_close_bracket) { + /* Get an integer value */ + len = (_object_t*)ast->data; + if(!_try_get_value(len, &val, _DT_INT)) { + _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(val.integer <= 0) { + _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(dummy.dimension_count >= MB_MAX_DIMENSION_COUNT) { + _handle_error_on_obj(s, SE_RN_TOO_MANY_DIMENSIONS, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + dummy.dimensions[dummy.dimension_count++] = (int)val.integer; + if(dummy.count) + dummy.count *= (unsigned)val.integer; + else + dummy.count += (unsigned)val.integer; + ast = ast->next; + /* Comma? */ + if(_IS_SEP(ast->data, ',')) + ast = ast->next; + } + /* Create or modify raw data */ + _clear_array(arr->data.array); +#ifdef MB_ENABLE_ARRAY_REF + dummy.ref = arr->data.array->ref; +#endif /* MB_ENABLE_ARRAY_REF */ + *(arr->data.array) = dummy; + _init_array(arr->data.array); + if(!arr->data.array->raw) { + arr->data.array->dimension_count = 0; + arr->data.array->dimensions[0] = 0; + arr->data.array->count = 0; + _handle_error_on_obj(s, SE_RN_OUT_OF_MEMORY, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + +_exit: + *l = ast; + + return result; +} + +/* IF statement */ +static int _core_if(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* val = 0; + _object_t* obj = 0; + bool_t multi_line = false; + bool_t skip = false; + _running_context_t* running = 0; + + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)*l; + ast = ast->next; + + val = _create_object(); + +_elseif: + _MAKE_NIL(val); + result = _calc_expression(s, &ast, &val); + if(result != MB_FUNC_OK) + goto _exit; + + obj = (_object_t*)ast->data; + if(val->data.integer) { + skip = true; + + if(!_IS_FUNC(obj, _core_then)) { + _handle_error_on_obj(s, SE_RN_INTEGER_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + if(ast && ast->next && _IS_EOS(ast->next->data)) + multi_line = true; + else + s->skip_to_eoi = _ls_back(s->sub_stack); + do { + ast = ast->next; + while(ast && _IS_EOS(ast->data)) + ast = ast->next; + if(ast && _IS_FUNC(ast->data, _core_endif)) { + ast = ast->prev; + + break; + } + result = _execute_statement(s, &ast, true); + if(result != MB_FUNC_OK) + goto _exit; + if(ast) + ast = ast->prev; + } while(ast && ( + (!multi_line && _IS_SEP(ast->data, ':')) || ( + multi_line && ast->next && ( + !_IS_FUNC(ast->next->data, _core_elseif) && + !_IS_FUNC(ast->next->data, _core_else) && + !_IS_FUNC(ast->next->data, _core_endif) + ) + ) + ) + ); + + if(!ast) + goto _exit; + + obj = (_object_t*)ast->data; + if(obj->type != _DT_EOS) { + s->skip_to_eoi = 0; + result = _skip_to(s, &ast, 0, _DT_EOS); + if(result != MB_FUNC_OK) + goto _exit; + } + } else { + if(ast && ast->next && _IS_EOS(ast->next->data)) { + multi_line = true; + + _skip_if_chunk(s, &ast); + } + if(multi_line && ast && _IS_FUNC(ast->data, _core_elseif)) { + ast = ast->next; + + goto _elseif; + } + if(multi_line && ast && _IS_FUNC(ast->data, _core_endif)) + goto _exit; + + result = _skip_to(s, &ast, _core_else, _DT_EOS); + if(result != MB_FUNC_OK) + goto _exit; + + obj = (_object_t*)ast->data; + if(obj->type != _DT_EOS) { + skip = true; + + if(!_IS_FUNC(obj, _core_else)) { + _handle_error_on_obj(s, SE_RN_ELSE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + do { + ast = ast->next; + while(_IS_EOS(ast->data)) + ast = ast->next; + if(ast && _IS_FUNC(ast->data, _core_endif)) { + ast = ast->prev; + + break; + } + result = _execute_statement(s, &ast, true); + if(result != MB_FUNC_OK) + goto _exit; + if(ast) + ast = ast->prev; + } while(ast && ( + (!multi_line && _IS_SEP(ast->data, ':')) || + (multi_line && !_IS_FUNC(ast->next->data, _core_endif)) + ) + ); + } + } + +_exit: + if(result == MB_SUB_RETURN) { + if(ast) + ast = ast->prev; + } else { + if(multi_line) { + int ret = MB_FUNC_OK; + if(skip) + ret = _skip_struct(s, &ast, _core_if, _core_endif); + if(result != MB_FUNC_END && result != MB_LOOP_BREAK && result != MB_LOOP_CONTINUE && result != MB_SUB_RETURN) + result = ret; + } + } + + *l = ast; + + if(val->type != _DT_UNKNOWN) + _destroy_object(val, 0); + + return result; +} + +/* THEN statement */ +static int _core_then(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* ELSEIF statement */ +static int _core_elseif(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* ELSE statement */ +static int _core_else(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* ENDIF statement */ +static int _core_endif(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* FOR statement */ +static int _core_for(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _var_t* var_loop = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + ast = ast->next; + + obj = (_object_t*)ast->data; + if(obj->type != _DT_VAR) { + _handle_error_on_obj(s, SE_RN_LOOP_VAR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + var_loop = obj->data.variable; + +#ifdef MB_ENABLE_COLLECTION_LIB + if(ast && ast->next && _IS_FUNC(ast->next->data, _core_in)) + result = _execute_ranged_for_loop(s, &ast, var_loop); + else + result = _execute_normal_for_loop(s, &ast, var_loop); +#else /* MB_ENABLE_COLLECTION_LIB */ + result = _execute_normal_for_loop(s, &ast, var_loop); +#endif /* MB_ENABLE_COLLECTION_LIB */ + +_exit: + *l = ast; + + return result; +} + +/* IN statement */ +static int _core_in(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* TO statement */ +static int _core_to(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* STEP statement */ +static int _core_step(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* NEXT statement */ +static int _core_next(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _running_context_t* running = 0; + + mb_assert(s && l); + + running = s->running_context; + ast = (_ls_node_t*)*l; + + result = MB_LOOP_CONTINUE; + + ast = ast->next; + if(ast && ((_object_t*)ast->data)->type == _DT_VAR) { + obj = (_object_t*)ast->data; + running->next_loop_var = obj->data.variable; + } + + *l = ast; + + return result; +} + +/* WHILE statement */ +static int _core_while(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* loop_begin_node = 0; + _object_t* obj = 0; + _object_t loop_cond; + _object_t* loop_cond_ptr = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + ast = ast->next; + + loop_cond_ptr = &loop_cond; + _MAKE_NIL(loop_cond_ptr); + + loop_begin_node = ast; + +_loop_begin: + ast = loop_begin_node; + + result = _calc_expression(s, &ast, &loop_cond_ptr); + if(result != MB_FUNC_OK) + goto _exit; + + if(loop_cond_ptr->data.integer) { + /* Keep looping */ + obj = (_object_t*)ast->data; + while(!_IS_FUNC(obj, _core_wend)) { + result = _execute_statement(s, &ast, true); + if(result == MB_LOOP_BREAK) { /* EXIT */ + if(_skip_struct(s, &ast, _core_while, _core_wend) != MB_FUNC_OK) + goto _exit; + _skip_to(s, &ast, 0, _DT_EOS); + result = MB_FUNC_OK; + + goto _exit; + } else if(result == MB_SUB_RETURN && s->last_routine) { /* RETURN */ + if(ast) ast = ast->prev; + if(ast) ast = ast->prev; + + goto _exit; + } else if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { /* Normally */ + goto _exit; + } + + if(!ast) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + obj = (_object_t*)ast->data; + } + + goto _loop_begin; + } else { + /* End looping */ + if(_skip_struct(s, &ast, _core_while, _core_wend) != MB_FUNC_OK) + goto _exit; + _skip_to(s, &ast, 0, _DT_EOS); + + goto _exit; + } + +_exit: + *l = ast; + + return result; +} + +/* WEND statement */ +static int _core_wend(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* DO statement */ +static int _core_do(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* loop_begin_node = 0; + _object_t* obj = 0; + _object_t loop_cond; + _object_t* loop_cond_ptr = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + ast = ast->next; + + obj = (_object_t*)ast->data; + if(!_IS_EOS(obj)) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + + loop_cond_ptr = &loop_cond; + _MAKE_NIL(loop_cond_ptr); + + loop_begin_node = ast; + +_loop_begin: + ast = loop_begin_node; + + obj = (_object_t*)ast->data; + while(!_IS_FUNC(obj, _core_until)) { + result = _execute_statement(s, &ast, true); + if(result == MB_LOOP_BREAK) { /* EXIT */ + if(_skip_struct(s, &ast, _core_do, _core_until) != MB_FUNC_OK) + goto _exit; + _skip_to(s, &ast, 0, _DT_EOS); + result = MB_FUNC_OK; + + goto _exit; + } else if(result == MB_SUB_RETURN && s->last_routine) { /* RETURN */ + if(ast) ast = ast->prev; + if(ast) ast = ast->prev; + + goto _exit; + } else if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { /* Normally */ + goto _exit; + } + + obj = (_object_t*)ast->data; + } + + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_until)) { + _handle_error_on_obj(s, SE_RN_UNTIL_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + + result = _calc_expression(s, &ast, &loop_cond_ptr); + if(result != MB_FUNC_OK) + goto _exit; + + if(loop_cond_ptr->data.integer) { + /* End looping */ + if(ast) + _skip_to(s, &ast, 0, _DT_EOS); + + goto _exit; + } else { + /* Keep looping */ + goto _loop_begin; + } + +_exit: + *l = ast; + + return result; +} + +/* UNTIL statement */ +static int _core_until(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* EXIT statement */ +static int _core_exit(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + result = MB_LOOP_BREAK; + + return result; +} + +/* GOTO statement */ +static int _core_goto(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _running_context_t* running = 0; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _label_t* label = 0; + _ls_node_t* glbsyminscope = 0; + + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)*l; + ast = ast->next; + + _using_jump_set_of_instructional(s, ast, _exit, result); + + obj = (_object_t*)ast->data; + if(obj->type != _DT_LABEL) { + _handle_error_on_obj(s, SE_RN_JUMP_LABEL_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + label = (_label_t*)obj->data.label; + if(!label->node) { + glbsyminscope = _ht_find(running->var_dict, label->name); + if(!(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_LABEL)) { + _handle_error_on_obj(s, SE_RN_LABEL_NOT_EXISTS, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + label->node = ((_object_t*)glbsyminscope->data)->data.label->node; + } + + mb_assert(label->node && label->node->prev); + ast = label->node->prev; + if(ast && !ast->data) + ast = ast->next; + +_exit: + *l = ast; + + return result; +} + +/* GOSUB statement */ +static int _core_gosub(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + + mb_assert(s && l); + + running = s->running_context; + ast = (_ls_node_t*)*l; + + _using_jump_set_of_instructional(s, ast, _exit, result); + + result = _core_goto(s, l); + if(result == MB_FUNC_OK) + _ls_pushback(s->sub_stack, ast); + +_exit: + return result; +} + +/* RETURN statement */ +static int _core_return(mb_interpreter_t* s, void** l) { + int result = MB_SUB_RETURN; + _ls_node_t* ast = 0; + _ls_node_t* sub_stack = 0; + _running_context_t* running = 0; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + running = s->running_context; + sub_stack = s->sub_stack; + + if(running->prev) { + ast = (_ls_node_t*)*l; + ast = ast->next; + if(mb_has_arg(s, (void**)&ast)) { + mb_check(mb_pop_value(s, (void**)&ast, &arg)); + mb_check(mb_push_value(s, (void**)&ast, arg)); + } + } + ast = (_ls_node_t*)_ls_popback(sub_stack); + if(!ast) { + _handle_error_on_obj(s, SE_RN_NO_RETURN_POINT, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + *l = ast; + +_exit: + return result; +} + +/* CALL statement */ +static int _core_call(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _routine_t* routine = 0; + mb_value_t ret; + _ls_node_t* pathed = 0; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + ast = ast->next; + + obj = (_object_t*)ast->data; + +_retry: + switch(obj->type) { + case _DT_FUNC: + if(_IS_FUNC(obj, _core_open_bracket)) { + mb_check(mb_attempt_open_bracket(s, l)); + + ast = ast->next; + obj = (_object_t*)ast->data; +#ifdef MB_ENABLE_CLASS + if(obj->type == _DT_VAR) { + pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PN(obj->data.variable->pathing), 0, 0); + if(pathed && pathed->data) + obj = (_object_t*)pathed->data; + } +#endif /* MB_ENABLE_CLASS */ + if(!obj || obj->type != _DT_ROUTINE) { + _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ret.type = MB_DT_ROUTINE; + ret.value.routine = obj->data.routine; + ast = ast->next; + *l = ast; + + mb_check(mb_attempt_close_bracket(s, l)); + + mb_check(mb_push_value(s, l, ret)); + + ast = (_ls_node_t*)*l; + } + + break; + case _DT_VAR: + if(obj->data.variable->data->type == _DT_ROUTINE) { + obj = obj->data.variable->data; + + goto _retry; + } +#ifdef MB_ENABLE_CLASS + pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PN(obj->data.variable->pathing), 0, 0); + if(pathed && pathed->data) + obj = (_object_t*)pathed->data; + /* Fall through */ +#else /* MB_ENABLE_CLASS */ + mb_unrefvar(pathed); +#endif /* MB_ENABLE_CLASS */ + case _DT_ROUTINE: + obj = _GET_ROUTINE(obj); + routine = obj->data.routine; +#ifdef MB_ENABLE_CLASS +# ifdef MB_ENABLE_LAMBDA + if(routine->type != _IT_LAMBDA) { +# else /* MB_ENABLE_LAMBDA */ + { +# endif /* MB_ENABLE_LAMBDA */ + bool_t is_a0 = false; + bool_t is_a1 = false; + if(s->last_instance && routine->instance) { + _traverse_class(s->last_instance->created_from, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, routine->instance->created_from, &is_a0); + _traverse_class(routine->instance->created_from, 0, _is_a_class, _META_LIST_MAX_DEPTH, true, s->last_instance->created_from, &is_a1); + } + if(routine->instance && ( + !s->last_instance || ( + s->last_instance && + !is_a0 && !is_a1 && + s->last_instance->created_from != routine->instance && + routine->instance->created_from != s->last_instance + ) + ) + ) { + pathed = _search_identifier_in_class(s, routine->instance, routine->name, 0, 0); + } else { + pathed = _search_identifier_in_scope_chain(s, 0, routine->name, _PATHING_NONE, 0, 0); + } + if(pathed && pathed->data) { + obj = (_object_t*)pathed->data; + obj = _GET_ROUTINE(obj); + routine = obj->data.routine; + } + } +#endif /* MB_ENABLE_CLASS */ +#ifdef MB_ENABLE_CLASS + s->calling = true; +#endif /* MB_ENABLE_CLASS */ + result = _eval_routine(s, &ast, 0, 0, routine, _has_routine_lex_arg, _pop_routine_lex_arg); +#ifdef MB_ENABLE_CLASS + s->calling = false; +#endif /* MB_ENABLE_CLASS */ + if(ast) + ast = ast->prev; + + break; + default: /* Do nothing */ + break; + } + + *l = ast; + +_exit: + return result; +} + +/* DEF statement */ +static int _core_def(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + _object_t* obj = 0; + _var_t* var = 0; + _ls_node_t* rnode = 0; + _routine_t* routine = 0; + + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)*l; + ast = ast->next; + + _using_jump_set_of_structured(s, ast, _exit, result); + + obj = (_object_t*)ast->data; + if(!_IS_ROUTINE(obj)) { + _handle_error_on_obj(s, SE_RN_ROUTINE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(obj->data.routine->func.basic.entry) { + _handle_error_on_obj(s, SE_RN_DUPLICATE_ROUTINE, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + routine = (_routine_t*)((_object_t*)ast->data)->data.routine; + ast = ast->next; + obj = (_object_t*)ast->data; + if(!_IS_FUNC(obj, _core_open_bracket)) { + _handle_error_on_obj(s, SE_RN_OPEN_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + obj = (_object_t*)ast->data; + while(!_IS_FUNC(obj, _core_close_bracket)) { + if(obj->type == _DT_VAR) { + var = obj->data.variable; + rnode = _search_identifier_in_scope_chain(s, routine->func.basic.scope, var->name, _PATHING_NONE, 0, 0); + if(rnode) + var = ((_object_t*)rnode->data)->data.variable; + if(!routine->func.basic.parameters) + routine->func.basic.parameters = _ls_create(); + _ls_pushback(routine->func.basic.parameters, var); + } else if(_IS_FUNC(obj, _core_args)) { + if(!routine->func.basic.parameters) + routine->func.basic.parameters = _ls_create(); + _ls_pushback(routine->func.basic.parameters, (void*)&_VAR_ARGS); + ast = ast->next; + obj = (_object_t*)ast->data; + + break; + } + + ast = ast->next; + obj = (_object_t*)ast->data; + } + ast = ast->next; + routine->func.basic.entry = ast; + + _skip_to(s, &ast, _core_enddef, _DT_INVALID); + + ast = ast->next; + +_exit: + *l = ast; + + return result; +} + +/* ENDDEF statement */ +static int _core_enddef(mb_interpreter_t* s, void** l) { + int result = MB_SUB_RETURN; + _ls_node_t* ast = 0; + _ls_node_t* sub_stack = 0; + + mb_assert(s && l); + + sub_stack = s->sub_stack; + + ast = (_ls_node_t*)_ls_popback(sub_stack); + if(!ast) { + _handle_error_on_obj(s, SE_RN_NO_RETURN_POINT, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + *l = ast; + +_exit: + return result; +} + +/* ... (variable argument list) statement */ +static int _core_args(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* var_args = 0; + bool_t pushed = false; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + if(ast) ast = ast->next; + *l = ast; + + var_args = s->var_args; + if(var_args) { + _object_t* obj = (_object_t*)_ls_popfront(var_args); + if(obj) { + mb_value_t arg; + mb_make_nil(arg); + _internal_object_to_public_value(obj, &arg); + mb_check(mb_push_value(s, l, arg)); + _UNREF(obj); + pushed = true; + _destroy_object_capsule_only(obj, 0); + } + } + + if(!pushed) { + mb_value_t arg; + mb_make_nil(arg); + arg.type = MB_DT_UNKNOWN; + mb_check(mb_push_value(s, l, arg)); + } + + return result; +} + +#ifdef MB_ENABLE_CLASS +/* CLASS statement */ +static int _core_class(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _running_context_t* running = 0; + _object_t* obj = 0; + _class_t* instance = 0; + _class_t* inherit = 0; + _class_t* last_inst = 0; + + mb_assert(s && l); + + running = s->running_context; + + ast = (_ls_node_t*)*l; + ast = ast->next; + + _using_jump_set_of_structured(s, ast, _exit, result); + + obj = (_object_t*)ast->data; + obj = _GET_CLASS(obj); + if(!_IS_CLASS(obj)) { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + instance = obj->data.instance; + ast = ast->next; + obj = (_object_t*)ast->data; + + last_inst = s->last_instance; + s->last_instance = instance; + + /* Process meta prototype list */ + if(_IS_FUNC(obj, _core_open_bracket)) { + do { + ast = ast->next; + obj = (_object_t*)ast->data; + if(obj && obj->type == _DT_VAR) { + _ls_node_t* tmp =_search_identifier_in_scope_chain(s, _OUTTER_SCOPE(running), obj->data.variable->name, _PATHING_NONE, 0, 0); + if(tmp && tmp->data) + obj = (_object_t*)tmp->data; + } + obj = _GET_CLASS(obj); + if(!_IS_CLASS(obj)) { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + inherit = obj->data.instance; + _link_meta_class(s, instance, inherit); + ast = ast->next; + obj = (_object_t*)ast->data; + } while(_IS_CLASS(obj) || _IS_SEP(obj, ',')); + if(_IS_FUNC(obj, _core_close_bracket)) { + ast = ast->next; + } else { + _handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + } + + *l = ast; + + /* Execute class body */ + running = _push_scope_by_class(s, instance->scope); + do { + result = _execute_statement(s, (_ls_node_t**)l, true); + if(result != MB_FUNC_OK && s->error_handler) { + if(result >= MB_EXTENDED_ABORT) + s->last_error = SE_EA_EXTENDED_ABORT; + _handle_error_now(s, s->last_error, s->last_error_file, result); + + goto _pop_exit; + } + ast = (_ls_node_t*)*l; + if(!ast) + break; + obj = (_object_t*)ast->data; + } while(ast && !_IS_FUNC(obj, _core_endclass)); + _pop_scope(s, false); + + /* Search for meta functions */ + if(_search_class_hash_and_compare_functions(s, instance) != MB_FUNC_OK) { + _handle_error_on_obj(s, SE_RN_HASH_AND_COMPARE_MUST_COME_TOGETHER, s->source_file, DON(ast), MB_FUNC_WARNING, _exit, result); + } + + /* Finished */ + if(ast) { + _skip_to(s, &ast, _core_endclass, _DT_NIL); + + ast = ast->next; + } + +_pop_exit: + if(result != MB_FUNC_OK) + _pop_scope(s, false); + +_exit: + *l = ast; + + s->last_instance = last_inst; + + return result; +} + +/* ENDCLASS statement */ +static int _core_endclass(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + _do_nothing(s, l, _exit, result); + +_exit: + return result; +} + +/* NEW statement */ +static int _core_new(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + _object_t obj; + _object_t tgt; + mb_value_t ret; + _class_t* instance = 0; + + mb_assert(s && l); + + mb_make_nil(ret); + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_func_end(s, l)); + + _MAKE_NIL(&obj); + _MAKE_NIL(&tgt); + switch(arg.type) { + case MB_DT_STRING: + arg.value.string = mb_strupr(arg.value.string); + if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) + goto _default; + _ref(&instance->ref, instance); + /* Fall through */ + case MB_DT_CLASS: + _public_value_to_internal_object(&arg, &obj); + _clone_object(s, &obj, &tgt, false, true); + ret.type = MB_DT_CLASS; + ret.value.instance = tgt.data.instance; + + break; + default: +_default: + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + _assign_public_value(&ret, 0); + +_exit: + _assign_public_value(&arg, 0); + + return result; +} + +/* VAR statement */ +static int _core_var(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_IGNORE; + _ls_node_t* ast = 0; + mb_unrefvar(s); + + ast = (_ls_node_t*)*l; + ast = ast->next; + + if(!s->last_instance) { + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + } + +_exit: + *l = ast; + + return result; +} + +/* REFLECT statement */ +static int _core_reflect(mb_interpreter_t* s, void** l) { +#ifdef MB_ENABLE_COLLECTION_LIB + int result = MB_FUNC_OK; + mb_value_t arg; + _object_t obj; + mb_value_t ret; + _class_t* instance = 0; + _dict_t* coll = 0; + + mb_assert(s && l); + + mb_make_nil(ret); + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_func_end(s, l)); + + _MAKE_NIL(&obj); + switch(arg.type) { + case MB_DT_STRING: + arg.value.string = mb_strupr(arg.value.string); + if((instance = _reflect_string_to_class(s, arg.value.string, &arg)) == 0) + goto _default; + _ref(&instance->ref, instance); + /* Fall through */ + case MB_DT_CLASS: + _public_value_to_internal_object(&arg, &obj); + coll = _create_dict(s); + _traverse_class(obj.data.instance, _reflect_class_field, 0, _META_LIST_MAX_DEPTH, false, coll, 0); + ret.type = MB_DT_DICT; + ret.value.dict = coll; + + break; + default: +_default: + _handle_error_on_obj(s, SE_RN_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + _assign_public_value(&arg, 0); + + return result; +#else /* MB_ENABLE_COLLECTION_LIB */ + mb_unrefvar(s); + mb_unrefvar(l); + + return MB_FUNC_ERR; +#endif /* MB_ENABLE_COLLECTION_LIB */ +} +#endif /* MB_ENABLE_CLASS */ + +#ifdef MB_ENABLE_LAMBDA +/* LAMBDA statement */ +static int _core_lambda(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t ret; + _running_context_t* running = 0; + _routine_t* routine = 0; + _ls_node_t* ast = 0; + int brackets = 0; + _var_t* var = 0; + _object_t* obj = 0; + unsigned ul = 0; + bool_t popped = false; + + mb_assert(s && l); + + /* Create lambda struct */ + routine = (_routine_t*)mb_malloc(sizeof(_routine_t)); + running = _init_lambda(s, routine); + + /* Parameter list */ + _mb_check_mark(mb_attempt_open_bracket(s, l), result, _error); + + while(mb_has_arg(s, l)) { +#ifdef MB_ENABLE_CLASS + bool_t pathing = false; +#endif /* MB_ENABLE_CLASS */ + void* v = 0; + + if(!routine->func.lambda.parameters) + routine->func.lambda.parameters = _ls_create(); + + ast = (_ls_node_t*)*l; + if(ast && _IS_FUNC(ast->data, _core_args)) { + _ls_pushback(routine->func.lambda.parameters, (void*)&_VAR_ARGS); + ast = ast->next; + *l = ast; + + break; + } + + _mb_check_mark(mb_get_var(s, l, &v), result, _error); + + if(!v || ((_object_t*)v)->type != _DT_VAR) { + _handle_error_on_obj(s, SE_RN_INVALID_LAMBDA, s->source_file, DON2(l), MB_FUNC_ERR, _error, result); + } + var = ((_object_t*)v)->data.variable; +#ifdef MB_ENABLE_CLASS + pathing = var->pathing; +#endif /* MB_ENABLE_CLASS */ + + /* Add lambda parameters */ + obj = 0; + var = _create_var(&obj, var->name, 0, true); +#ifdef MB_ENABLE_CLASS + var->pathing = pathing; +#endif /* MB_ENABLE_CLASS */ + ul = _ht_set_or_insert(routine->func.lambda.scope->var_dict, var->name, obj); + mb_assert(ul); + _ls_pushback(routine->func.lambda.parameters, var); + + ast = (_ls_node_t*)*l; + if(_IS_FUNC(ast->data, _core_close_bracket)) + break; + *l = ast; + } + + _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); + + /* Lambda body */ + ast = (_ls_node_t*)*l; + if(ast) ast = ast->prev; + while(ast && _IS_EOS(ast->next->data)) + ast = ast->next; + *l = ast; + + _mb_check_mark(mb_attempt_open_bracket(s, l), result, _error); + + ast = (_ls_node_t*)*l; + routine->func.lambda.entry = ast; + while(ast && (brackets || !_IS_FUNC(ast->data, _core_close_bracket))) { + if(_IS_FUNC(ast->data, _core_open_bracket)) + brackets++; + else if(_IS_FUNC(ast->data, _core_close_bracket)) + brackets--; + + if(ast && !_is_valid_lambda_body_node(s, &routine->func.lambda, (_object_t*)ast->data)) { + _handle_error_on_obj(s, SE_RN_INVALID_LAMBDA, s->source_file, DON2(l), MB_FUNC_ERR, _error, result); + } + + /* Mark upvalues */ + if(ast) + _try_mark_upvalue(s, routine, (_object_t*)ast->data); + + ast = ast->next; + } + *l = ast; + routine->func.lambda.end = ast; + + _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); + + _pop_scope(s, false); + popped = true; + + /* Push the return value */ + ret.type = MB_DT_ROUTINE; + ret.value.routine = routine; + + _mb_check_mark(mb_push_value(s, l, ret), result, _error); + + /* Error processing */ + while(0) { +_error: + if(!popped) + _pop_scope(s, false); + if(routine) + _destroy_routine(s, routine); + } + + return result; +} +#endif /* MB_ENABLE_LAMBDA */ + +#ifdef MB_ENABLE_ALLOC_STAT +/* MEM statement */ +static int _core_mem(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)); + + mb_check(mb_push_int(s, l, (int_t)_mb_allocated)); + + return result; +} +#endif /* MB_ENABLE_ALLOC_STAT */ + +/* TYPE statement */ +static int _core_type(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + int i = 0; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + if(arg.type == MB_DT_STRING) { + mb_data_e types[] = { + MB_DT_NIL, + MB_DT_UNKNOWN, + MB_DT_INT, + MB_DT_REAL, + MB_DT_NUM, + MB_DT_STRING, + MB_DT_TYPE, + MB_DT_USERTYPE, +#ifdef MB_ENABLE_USERTYPE_REF + MB_DT_USERTYPE_REF, +#endif /* MB_ENABLE_USERTYPE_REF */ + MB_DT_ARRAY, +#ifdef MB_ENABLE_COLLECTION_LIB + MB_DT_LIST, + MB_DT_LIST_IT, + MB_DT_DICT, + MB_DT_DICT_IT, + MB_DT_COLLECTION, + MB_DT_ITERATOR, +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + MB_DT_CLASS, +#endif /* MB_ENABLE_CLASS */ + MB_DT_ROUTINE + }; + for(i = 0; i < countof(types); i++) { + unsigned e = types[i]; + if(!mb_stricmp(mb_get_type_string((mb_data_e)e), arg.value.string)) { + arg.value.type = (mb_data_e)e; + arg.type = MB_DT_TYPE; + + goto _found; + } + } + } + os = _try_overridden(s, l, &arg, _CORE_ID_TYPE, MB_MF_FUNC); + if((os & MB_MS_DONE) == MB_MS_NONE) { + arg.value.type = arg.type; + arg.type = MB_DT_TYPE; + } + +_found: + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, arg)); + } + + return result; +} + +/* IMPORT statement */ +static int _core_import(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)); + + return result; +} + +/* END statement */ +static int _core_end(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + + mb_assert(s && l); + + result = MB_FUNC_END; + + return result; +} + +/** Standard lib */ + +/* Get the absolute value of a number */ +static int _std_abs(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: + arg.value.integer = (int_t)abs(arg.value.integer); + + break; + case MB_DT_REAL: + arg.value.float_point = (real_t)fabs(arg.value.float_point); + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the sign of a number */ +static int _std_sgn(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: + arg.value.integer = sgn(arg.value.integer); + + break; + case MB_DT_REAL: + arg.value.integer = sgn(arg.value.float_point); + arg.type = MB_DT_INT; + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + +_exit: + return result; +} + +/* Get the square root of a number */ +static int _std_sqr(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, sqrt, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the greatest integer not greater than a number */ +static int _std_floor(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: /* Do nothing */ + break; + case MB_DT_REAL: + arg.value.integer = (int_t)floor(arg.value.float_point); + arg.type = MB_DT_INT; + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + +_exit: + return result; +} + +/* Get the least integer not less than a number */ +static int _std_ceil(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: /* Do nothing */ + break; + case MB_DT_REAL: + arg.value.integer = (int_t)ceil(arg.value.float_point); + arg.type = MB_DT_INT; + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + +_exit: + return result; +} + +/* Get the integer format of a number */ +static int _std_fix(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: /* Do nothing */ + break; + case MB_DT_REAL: + arg.value.integer = (int_t)(arg.value.float_point); + arg.type = MB_DT_INT; + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + +_exit: + return result; +} + +/* Get the rounded integer of a number */ +static int _std_round(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: /* Do nothing */ + break; + case MB_DT_REAL: + arg.value.integer = (int_t)(arg.value.float_point + (real_t)0.5f); + arg.type = MB_DT_INT; + + break; + default: + _handle_error_on_obj(s, SE_RN_NUMBER_EXPECTED, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + + break; + } + mb_check(mb_push_int(s, l, arg.value.integer)); + +_exit: + return result; +} + +/* Set a random seed */ +static int _std_srnd(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + int_t seed = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_int(s, l, &seed)); + + mb_check(mb_attempt_close_bracket(s, l)); + + srand((unsigned)seed); + + return result; +} + +/* Get a random value among 0 ~ 1 or among given bounds */ +static int _std_rnd(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + real_t rnd = (real_t)0.0f; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + + if(ast && ast->next && _IS_FUNC(ast->next->data, _core_open_bracket)) { + int_t lw = 0; + int_t hg = 0; + + mb_check(mb_attempt_open_bracket(s, l)); + + if(mb_has_arg(s, l)) { + mb_check(mb_pop_int(s, l, &hg)); + } + if(mb_has_arg(s, l)) { + lw = hg; + mb_check(mb_pop_int(s, l, &hg)); + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if(lw >= hg) { + _handle_error_on_obj(s, SE_RN_ILLEGAL_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + rnd = (real_t)rand() / RAND_MAX * (hg - lw + (real_t)0.99999f) + lw; /* [LOW, HIGH] */ + + mb_check(mb_push_int(s, l, (int_t)rnd)); + } else { + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_attempt_func_end(s, l)); + + rnd = (real_t)(((real_t)(rand() % 101)) / 100.0f); /* [0.0, 1.0] */ + + mb_check(mb_push_real(s, l, rnd)); + } + +_exit: + return result; +} + +/* Get the sin value of a number */ +static int _std_sin(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, sin, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the cos value of a number */ +static int _std_cos(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, cos, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the tan value of a number */ +static int _std_tan(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, tan, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the asin value of a number */ +static int _std_asin(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, asin, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the acos value of a number */ +static int _std_acos(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, acos, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the atan value of a number */ +static int _std_atan(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, atan, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the exp value of a number */ +static int _std_exp(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, exp, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the log value of a number */ +static int _std_log(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _math_calculate_fun_real(s, l, arg, log, _exit, result); + + mb_check(mb_push_value(s, l, arg)); + +_exit: + return result; +} + +/* Get the ASCII code of a character */ +static int _std_asc(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + char* arg = 0; + int_t val = 0; +#ifdef MB_ENABLE_UNICODE + size_t sz = 0; +#endif /* MB_ENABLE_UNICODE */ + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_string(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + if(arg[0] == _ZERO_CHAR) { + result = MB_FUNC_ERR; + + goto _exit; + } +#ifdef MB_ENABLE_UNICODE + sz = (size_t)mb_uu_ischar(arg); + if(sizeof(int_t) < sz) { + sz = sizeof(int_t); + _handle_error_on_obj(s, SE_RN_NUMBER_OVERFLOW, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + } + memcpy(&val, arg, sz); +#else /* MB_ENABLE_UNICODE */ + val = (int_t)arg[0]; +#endif /* MB_ENABLE_UNICODE */ + mb_check(mb_push_int(s, l, val)); + +_exit: + return result; +} + +/* Get the character of an ASCII code */ +static int _std_chr(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + int_t arg = 0; + char* chr = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_int(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + chr = (char*)mb_malloc(sizeof(arg) + 1); + memset(chr, 0, sizeof(arg) + 1); + memcpy(chr, &arg, sizeof(arg)); + mb_check(mb_push_string(s, l, chr)); + + return result; +} + +/* Get a number of characters from the left of a string */ +static int _std_left(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + char* arg = 0; + int_t count = 0; + char* sub = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_string(s, l, &arg)); + mb_check(mb_pop_int(s, l, &count)); + + mb_check(mb_attempt_close_bracket(s, l)); + + if(count <= 0) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +#ifdef MB_ENABLE_UNICODE + switch(mb_uu_substr(arg, 0, (int)count, &sub)) { + case 0: + _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + case -1: + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_UNICODE */ + sub = (char*)mb_malloc(count + 1); + memcpy(sub, arg, count); + sub[count] = _ZERO_CHAR; +#endif /* MB_ENABLE_UNICODE */ + mb_check(mb_push_string(s, l, sub)); + +_exit: + return result; +} + +/* Get a number of characters from a specific position of a string */ +static int _std_mid(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + char* arg = 0; + int_t start = 0; + int_t count = 0; + char* sub = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_string(s, l, &arg)); + mb_check(mb_pop_int(s, l, &start)); + mb_check(mb_pop_int(s, l, &count)); + + mb_check(mb_attempt_close_bracket(s, l)); + + if(count <= 0 || start < 0 || start >= (int_t)strlen(arg)) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +#ifdef MB_ENABLE_UNICODE + switch(mb_uu_substr(arg, start, (int)count, &sub)) { + case 0: + _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + case -1: + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_UNICODE */ + sub = (char*)mb_malloc(count + 1); + memcpy(sub, arg + start, count); + sub[count] = _ZERO_CHAR; +#endif /* MB_ENABLE_UNICODE */ + mb_check(mb_push_string(s, l, sub)); + +_exit: + return result; +} + +/* Get a number of characters from the right of a string */ +static int _std_right(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + char* arg = 0; + int_t count = 0; + char* sub = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_string(s, l, &arg)); + mb_check(mb_pop_int(s, l, &count)); + + mb_check(mb_attempt_close_bracket(s, l)); + + if(count <= 0) { + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + +#ifdef MB_ENABLE_UNICODE + switch(mb_uu_substr(arg, (int)(mb_uu_strlen(arg) - count), (int)count, &sub)) { + case 0: + _handle_error_on_obj(s, SE_RN_INVALID_STRING, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + case -1: + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } +#else /* MB_ENABLE_UNICODE */ + sub = (char*)mb_malloc(count + 1); + memcpy(sub, arg + (strlen(arg) - count), count); + sub[count] = _ZERO_CHAR; +#endif /* MB_ENABLE_UNICODE */ + mb_check(mb_push_string(s, l, sub)); + +_exit: + return result; +} + +/* Get the string format of a number */ +static int _std_str(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + _dynamic_buffer_t buf; + size_t lbuf = 32; + + mb_assert(s && l); + + mb_make_nil(arg); + + _INIT_BUF(buf); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + + mb_check(mb_attempt_close_bracket(s, l)); + + switch(arg.type) { + case MB_DT_INT: + lbuf = 32; /* Enough for even 64bit integer */ + _RESIZE_CHAR_BUF(buf, lbuf); + if((size_t)sprintf(_CHAR_BUF_PTR(buf), MB_INT_FMT, arg.value.integer) >= lbuf) { + mb_assert(0 && "Buffer overflow."); + } + + break; + case MB_DT_REAL: + lbuf = 1 /* - */ + (DBL_MAX_10_EXP + 1) /* 308 + 1 digits */ + 1 /* . */ + 6 /* precision */ + 1 /* \0 */; + _RESIZE_CHAR_BUF(buf, lbuf); + if((size_t)sprintf(_CHAR_BUF_PTR(buf), MB_REAL_FMT, arg.value.float_point) >= lbuf) { + mb_assert(0 && "Buffer overflow."); + } + + break; +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: { + bool_t got_tostr = false; + _class_t* instance = (_class_t*)arg.value.instance; + _object_t val_obj; + _MAKE_NIL(&val_obj); + if((result = _format_class_to_string(s, l, instance, &val_obj, &got_tostr)) == MB_FUNC_OK) { + if(got_tostr) { + mb_check(mb_push_string(s, l, val_obj.data.string)); + } else { + const char* sp = mb_get_type_string(_internal_type_to_public_type(_DT_CLASS)); + char* ret = mb_strdup(sp, strlen(sp) + 1); + mb_check(mb_push_string(s, l, ret)); + } + + goto _exit; + } else { + goto _exit; + } + } +#endif /* MB_ENABLE_CLASS */ + default: + result = MB_FUNC_ERR; + + goto _exit; + } + mb_check(mb_push_string(s, l, _HEAP_CHAR_BUF(buf))); + +_exit: + return result; +} + +/* Get the number format of a string, or get the value of a dictionary iterator */ +static int _std_val(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + char* conv_suc = 0; + mb_value_t arg; +#ifdef MB_ENABLE_COLLECTION_LIB + _object_t ocoi; +#endif /* MB_ENABLE_COLLECTION_LIB */ + mb_value_t ret; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(arg); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &arg)); + os = _try_overridden(s, l, &arg, _STD_ID_VAL, MB_MF_FUNC); + if((os & MB_MS_DONE) == MB_MS_NONE) { + switch(arg.type) { + case MB_DT_STRING: + ret.value.integer = (int_t)mb_strtol(arg.value.string, &conv_suc, 0); + if(*conv_suc == _ZERO_CHAR) { + ret.type = MB_DT_INT; + mb_check(mb_push_value(s, l, ret)); + + goto _exit; + } + ret.value.float_point = (real_t)mb_strtod(arg.value.string, &conv_suc); + if(*conv_suc == _ZERO_CHAR) { + ret.type = MB_DT_REAL; + mb_check(mb_push_value(s, l, ret)); + + goto _exit; + } + result = MB_FUNC_ERR; + + break; +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_DICT_IT: + _MAKE_NIL(&ocoi); + _public_value_to_internal_object(&arg, &ocoi); + if(ocoi.data.dict_it && ocoi.data.dict_it->curr_node && ocoi.data.dict_it->curr_node->data) { + _internal_object_to_public_value((_object_t*)ocoi.data.dict_it->curr_node->data, &ret); + mb_check(mb_push_value(s, l, ret)); + } else { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ + default: + _handle_error_on_obj(s, SE_RN_TYPE_NOT_MATCH, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, ret)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + return result; +} + +/* Get the length of a string or an array, or element count of a collection or a variable argument list */ +static int _std_len(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + mb_value_t arg; + _array_t* arr = 0; +#ifdef MB_ENABLE_COLLECTION_LIB + _list_t* lst = 0; + _dict_t* dct = 0; +#endif /* MB_ENABLE_COLLECTION_LIB */ + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + ast = (_ls_node_t*)*l; + if(ast) obj = (_object_t*)ast->data; + if(obj && _IS_FUNC(obj, _core_args)) { + ast = ast->next; + *l = ast; + mb_check(mb_push_int(s, l, s->var_args ? (int_t)_ls_count(s->var_args) : 0)); + + mb_check(mb_attempt_close_bracket(s, l)); + + goto _exit; + } + mb_check(mb_pop_value(s, l, &arg)); + os = _try_overridden(s, l, &arg, _STD_ID_LEN, MB_MF_FUNC); + if((os & MB_MS_DONE) == MB_MS_NONE) { + switch(arg.type) { + case MB_DT_STRING: +#ifdef MB_ENABLE_UNICODE + mb_check(mb_push_int(s, l, (int_t)mb_uu_strlen(arg.value.string))); +#else /* MB_ENABLE_UNICODE */ + mb_check(mb_push_int(s, l, (int_t)strlen(arg.value.string))); +#endif /* MB_ENABLE_UNICODE */ + + break; + case MB_DT_ARRAY: + arr = (_array_t*)arg.value.array; + mb_check(mb_push_int(s, l, (int_t)arr->count)); + + break; +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + lst = (_list_t*)arg.value.list; + mb_check(mb_push_int(s, l, (int_t)lst->count)); + _assign_public_value(&arg, 0); + + break; + case MB_DT_DICT: + dct = (_dict_t*)arg.value.dict; + mb_check(mb_push_int(s, l, (int_t)_ht_count(dct->dict))); + _assign_public_value(&arg, 0); + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ + default: + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + + break; + } + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_int(s, l, 0)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + return result; +} + +/* GET statement */ +static int _std_get(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t ov; + mb_value_t arg; + _object_t obj; +#ifdef MB_ENABLE_COLLECTION_LIB + int_t index = 0; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + char* field = 0; + _ls_node_t* fnode = 0; + _object_t* fobj = 0; +#endif /* MB_ENABLE_CLASS */ + mb_value_t ret; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(ov); + mb_make_nil(arg); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &ov)); + os = _try_overridden(s, l, &ov, _STD_ID_GET, MB_MF_FUNC); + if((os & MB_MS_DONE) == MB_MS_NONE) { + _MAKE_NIL(&obj); + switch(ov.type) { +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + _public_value_to_internal_object(&ov, &obj); + mb_check(mb_pop_int(s, l, &index)); + if(!_at_list(obj.data.list, index, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&ov, &obj); + mb_check(mb_pop_value(s, l, &arg)); + if(!_find_dict(obj.data.dict, &arg, &ret)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_LIST_IT: + _public_value_to_internal_object(&ov, &obj); + if(obj.data.list_it && !obj.data.list_it->list->range_begin && obj.data.list_it->curr.node && obj.data.list_it->curr.node->data) { + _internal_object_to_public_value((_object_t*)obj.data.list_it->curr.node->data, &ret); + } else if(obj.data.list_it && obj.data.list_it->list->range_begin) { + mb_make_int(ret, obj.data.list_it->curr.ranging); + } else { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; + case MB_DT_DICT_IT: + _public_value_to_internal_object(&ov, &obj); + if(obj.data.dict_it && obj.data.dict_it->curr_node && obj.data.dict_it->curr_node->extra) { + _internal_object_to_public_value((_object_t*)obj.data.dict_it->curr_node->extra, &ret); + } else { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + _public_value_to_internal_object(&ov, &obj); + mb_check(mb_pop_string(s, l, &field)); + field = mb_strupr(field); + fnode = _search_identifier_in_class(s, obj.data.instance, field, 0, 0); + if(fnode && fnode->data) { + fobj = (_object_t*)fnode->data; + _internal_object_to_public_value(fobj, &ret); + } + + break; +#endif /* MB_ENABLE_CLASS */ + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_OR_ITERATOR_OR_CLASS_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, ret)); + } + +_exit: + _assign_public_value(&ov, 0); + + return result; +} + +/* SET statement */ +static int _std_set(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t ov; + mb_value_t key; + mb_value_t val; + _object_t obj; +#ifdef MB_ENABLE_COLLECTION_LIB + _object_t* oval = 0; + int_t idx = 0; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + char* field = 0; + _ls_node_t* fnode = 0; + _object_t* fobj = 0; + mb_value_t nv; +#endif /* MB_ENABLE_CLASS */ + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(ov); + mb_make_nil(key); + mb_make_nil(val); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &ov)); + os = _try_overridden(s, l, &ov, _STD_ID_SET, MB_MF_FUNC); + if((os & MB_MS_DONE) == MB_MS_NONE) { + _MAKE_NIL(&obj); + switch(ov.type) { +#ifdef MB_ENABLE_COLLECTION_LIB + case MB_DT_LIST: + _public_value_to_internal_object(&ov, &obj); + while(mb_has_arg(s, l)) { + mb_make_nil(val); + mb_check(mb_pop_int(s, l, &idx)); + mb_check(mb_pop_value(s, l, &val)); + if(!_set_list(obj.data.list, idx, &val, &oval)) { + _destroy_object(oval, 0); + + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&ov, &obj); + while(mb_has_arg(s, l)) { + mb_make_nil(key); + mb_make_nil(val); + mb_check(mb_pop_value(s, l, &key)); + mb_check(mb_pop_value(s, l, &val)); + _set_dict(obj.data.dict, &key, &val, 0, 0); + } + + break; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + mb_make_nil(nv); + + _public_value_to_internal_object(&ov, &obj); + mb_check(mb_pop_string(s, l, &field)); + mb_check(mb_pop_value(s, l, &nv)); + field = mb_strupr(field); + fnode = _search_identifier_in_class(s, obj.data.instance, field, 0, 0); + if(fnode && _IS_VAR(fnode->data)) { + _object_t* nobj = 0; + fobj = (_object_t*)fnode->data; + _destroy_object(fobj->data.variable->data, 0); + _create_internal_object_from_public_value(&nv, &nobj); + fobj->data.variable->data = nobj; + } else { + _handle_error_on_obj(s, SE_RN_VARIABLE_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + break; +#endif /* MB_ENABLE_CLASS */ + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, ov)); + } + +_exit: + _assign_public_value(&ov, 0); + + return result; +} + +/* PRINT statement */ +static int _std_print(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + _object_t val_obj; + _object_t* val_ptr = 0; + bool_t pathed_str = false; + + mb_assert(s && l); + + ++s->no_eat_comma_mark; + ast = (_ls_node_t*)*l; + ast = ast->next; + if(!ast || !ast->data) { + _handle_error_on_obj(s, SE_RN_SYNTAX_ERROR, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + + obj = (_object_t*)ast->data; + do { + pathed_str = false; + val_ptr = &val_obj; + _MAKE_NIL(val_ptr); + switch(obj->type) { + case _DT_VAR: + if(obj->data.variable->data->type == _DT_ROUTINE) { + obj = obj->data.variable->data; + val_ptr = _eval_var_in_print(s, &val_ptr, &ast, obj); + + goto _print; + } +#ifdef MB_ENABLE_CLASS + if(obj->data.variable->pathing) { + _ls_node_t* pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, _PU(obj->data.variable->pathing), 0, 0); + if(pathed && pathed->data) { + if(obj != (_object_t*)pathed->data) { + obj = (_object_t*)pathed->data; + val_ptr = _eval_var_in_print(s, &val_ptr, &ast, obj); + if(val_ptr->type == _DT_STRING) + pathed_str = true; + } + } + + goto _print; + } +#endif /* MB_ENABLE_CLASS */ + /* Fall through */ + case _DT_ARRAY: /* Fall through */ + case _DT_NIL: /* Fall through */ + case _DT_INT: /* Fall through */ + case _DT_REAL: /* Fall through */ + case _DT_STRING: /* Fall through */ + case _DT_TYPE: /* Fall through */ +#ifdef MB_ENABLE_CLASS + case _DT_CLASS: /* Fall through */ +#endif /* MB_ENABLE_CLASS */ + case _DT_FUNC: /* Fall through */ + case _DT_ROUTINE: + result = _calc_expression(s, &ast, &val_ptr); + _REF(val_ptr); + _UNREF(val_ptr); +_print: + if(val_ptr->type == _DT_NIL) { + _get_printer(s)(MB_NIL); + } else if(val_ptr->type == _DT_INT) { + _get_printer(s)(MB_INT_FMT, val_ptr->data.integer); + } else if(val_ptr->type == _DT_REAL) { + _get_printer(s)(MB_REAL_FMT, val_ptr->data.float_point); + } else if(val_ptr->type == _DT_STRING) { + _print_string(s, val_ptr); + if(!val_ptr->ref && val_ptr->data.string && !pathed_str) { + safe_free(val_ptr->data.string); + } +#ifdef MB_ENABLE_USERTYPE_REF + } else if(val_ptr->type == _DT_USERTYPE_REF) { + if(val_ptr->data.usertype_ref->fmt) { + _dynamic_buffer_t buf; + size_t lbuf = 0; + _INIT_BUF(buf); + while((lbuf = (size_t)val_ptr->data.usertype_ref->fmt(s, val_ptr->data.usertype_ref->usertype, _CHAR_BUF_PTR(buf), (unsigned)_CHARS_OF_BUF(buf))) > _CHARS_OF_BUF(buf)) { + _RESIZE_CHAR_BUF(buf, lbuf); + } + _get_printer(s)("%s", _CHAR_BUF_PTR(buf)); + _DISPOSE_BUF(buf); + } else { + _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(val_ptr->type))); + } + _unref(&val_ptr->data.usertype_ref->ref, val_ptr->data.usertype_ref); +#endif /* MB_ENABLE_USERTYPE_REF */ + } else if(val_ptr->type == _DT_TYPE) { + _get_printer(s)(mb_get_type_string(val_ptr->data.type)); +#ifdef MB_ENABLE_CLASS + } else if(val_ptr->type == _DT_CLASS) { + bool_t got_tostr = false; + _class_t* instance = val_ptr->data.instance; + val_ptr = &val_obj; + _MAKE_NIL(val_ptr); + if((result = _format_class_to_string(s, (void**)&ast, instance, val_ptr, &got_tostr)) == MB_FUNC_OK) { + if(got_tostr) { + obj = val_ptr; + + goto _print; + } else { + _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(_DT_CLASS))); + } + } else { + goto _exit; + } +#endif /* MB_ENABLE_CLASS */ + } else { + _get_printer(s)(mb_get_type_string(_internal_type_to_public_type(val_ptr->type))); + } + if(result != MB_FUNC_OK) + goto _exit; + /* Fall through */ + case _DT_SEP: + if(!ast) + break; + obj = (_object_t*)ast->data; +#if _COMMA_AS_NEWLINE + if(obj->data.separator == ',') { +#else /* _COMMA_AS_NEWLINE */ + if(obj->data.separator == ';') { +#endif /* _COMMA_AS_NEWLINE */ + _get_printer(s)("\n"); + } + + break; + default: + _handle_error_on_obj(s, SE_CM_NOT_SUPPORTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + + break; + } + + if(!ast) + break; + obj = (_object_t*)ast->data; + if(_is_print_terminal(s, obj)) + break; + if(_IS_SEP(obj, ',') || _IS_SEP(obj, ';')) { + ast = ast->next; + obj = (_object_t*)ast->data; + } else { + _handle_error_on_obj(s, SE_RN_COMMA_OR_SEMICOLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + } while(ast && !_IS_SEP(obj, ':') && !_IS_FUNC(obj, _core_close_bracket) && (obj->type == _DT_SEP || !_is_expression_terminal(s, obj))); + +_exit: + --s->no_eat_comma_mark; + + *l = ast; + if(result != MB_FUNC_OK) + _get_printer(s)("\n"); + + return result; +} + +/* INPUT statement */ +static int _std_input(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _object_t* obj = 0; + char line[_INPUT_MAX_LENGTH]; + char* conv_suc = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_func_begin(s, l)); + + mb_check(mb_attempt_func_end(s, l)); + + ast = (_ls_node_t*)*l; + obj = (_object_t*)ast->data; + + if(!obj || obj->type == _DT_EOS) { +#ifdef MB_CP_VC + getch(); +#else /* MB_CP_VC */ + _get_inputer(s)(line, sizeof(line)); +#endif /* MB_CP_VC */ + + goto _exit; + } + if(obj->type == _DT_STRING) { + _print_string(s, obj); + ast = ast->next; + obj = (_object_t*)ast->data; + if(!_IS_SEP(obj, ',')) { + _handle_error_on_obj(s, SE_RN_COMMA_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + ast = ast->next; + obj = (_object_t*)ast->data; + } + if(obj->type != _DT_VAR) { + _handle_error_on_obj(s, SE_RN_VARIABLE_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result); + } + if(obj->data.variable->data->type == _DT_INT || obj->data.variable->data->type == _DT_REAL) { + _get_inputer(s)(line, sizeof(line)); + obj->data.variable->data->type = _DT_INT; + obj->data.variable->data->data.integer = (int_t)mb_strtol(line, &conv_suc, 0); + if(*conv_suc != _ZERO_CHAR) { + obj->data.variable->data->type = _DT_REAL; + obj->data.variable->data->data.float_point = (real_t)mb_strtod(line, &conv_suc); + if(*conv_suc != _ZERO_CHAR) { + result = MB_FUNC_ERR; + + goto _exit; + } + } + ast = ast->next; + } else if(obj->data.variable->data->type == _DT_STRING) { + size_t len = 0; + if(obj->data.variable->data->data.string) { + safe_free(obj->data.variable->data->data.string); + } + len = (size_t)_get_inputer(s)(line, sizeof(line)); +#if defined MB_CP_VC && defined MB_ENABLE_UNICODE + do { + _dynamic_buffer_t buf; + _dynamic_buffer_t wbuf; + _INIT_BUF(buf); + _INIT_BUF(wbuf); + while((len = (size_t)mb_bytes_to_wchar_ansi(line, &_WCHAR_BUF_PTR(wbuf), _WCHARS_OF_BUF(wbuf))) > _WCHARS_OF_BUF(wbuf)) { + _RESIZE_WCHAR_BUF(wbuf, len); + } + while((len = mb_wchar_to_bytes(_WCHAR_BUF_PTR(wbuf), &_CHAR_BUF_PTR(buf), _CHARS_OF_BUF(buf))) > _CHARS_OF_BUF(buf)) { + _RESIZE_CHAR_BUF(buf, len); + } + _DISPOSE_BUF(wbuf); + obj->data.variable->data->data.string = _HEAP_CHAR_BUF(buf); + } while(0); +#else /* MB_CP_VC && MB_ENABLE_UNICODE */ + obj->data.variable->data->data.string = mb_memdup(line, (unsigned)(len + 1)); +#endif /* MB_CP_VC && MB_ENABLE_UNICODE */ + ast = ast->next; + } else { + result = MB_FUNC_ERR; + + goto _exit; + } + +_exit: + *l = ast; + + return result; +} + +/** Collection lib */ + +#ifdef MB_ENABLE_COLLECTION_LIB +/* LIST statement */ +static int _coll_list(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + _list_t* coll = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + coll = _create_list(s); + + if(mb_has_arg(s, l)) { + _ls_node_t* ast = 0; + _object_t* obj = 0; + mb_make_nil(arg); + _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); + ast = (_ls_node_t*)*l; + if(ast) obj = (_object_t*)ast->data; + if(arg.type == MB_DT_INT && obj && _IS_FUNC(obj, _core_to)) { + /* Push a range of integer */ + int_t begin = arg.value.integer; + int_t end = 0; + int_t step = 0; + ast = ast->next; + _mb_check_mark(mb_pop_int(s, (void**)&ast, &end), result, _error); + step = sgn(end - begin); + coll->range_begin = (int_t*)mb_malloc(sizeof(int_t)); + *coll->range_begin = begin; + coll->count = end - begin + step; + if(!coll->count) coll->count = 1; + *l = ast; + } else { + /* Push arguments */ + _push_list(coll, &arg, 0); + while(mb_has_arg(s, l)) { + mb_make_nil(arg); + _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); + _push_list(coll, &arg, 0); + } + } + } + + _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); + + arg.type = MB_DT_LIST; + arg.value.list = coll; + _mb_check_mark(mb_push_value(s, l, arg), result, _error); + + while(0) { +_error: + mb_make_nil(arg); + mb_push_value(s, l, arg); + _destroy_list(coll); + } + + return result; +} + +/* DICT statement */ +static int _coll_dict(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t arg; + mb_value_t val; + _dict_t* coll = 0; + + mb_assert(s && l); + + mb_check(mb_attempt_open_bracket(s, l)); + + coll = _create_dict(s); + + while(mb_has_arg(s, l)) { + mb_make_nil(arg); + mb_make_nil(val); + _mb_check_mark(mb_pop_value(s, l, &arg), result, _error); + _mb_check_mark(mb_pop_value(s, l, &val), result, _error); + _set_dict(coll, &arg, &val, 0, 0); + } + + _mb_check_mark(mb_attempt_close_bracket(s, l), result, _error); + + arg.type = MB_DT_DICT; + arg.value.dict = coll; + _mb_check_mark(mb_push_value(s, l, arg), result, _error); + + while(0) { +_error: + mb_make_nil(arg); + mb_push_value(s, l, arg); + _destroy_dict(coll); + } + + return result; +} + +/* PUSH statement */ +static int _coll_push(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + mb_value_t arg; + _object_t olst; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_PUSH, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + if(coll.type != MB_DT_LIST) { + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _MAKE_NIL(&olst); + _public_value_to_internal_object(&coll, &olst); + + while(mb_has_arg(s, l)) { + mb_make_nil(arg); + mb_check(mb_pop_value(s, l, &arg)); + _push_list(olst.data.list, &arg, 0); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* POP statement */ +static int _coll_pop(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + mb_value_t val; + _object_t olst; + _object_t ocoll; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(val); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_POP, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + if(coll.type != MB_DT_LIST) { + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + _MAKE_NIL(&olst); + _public_value_to_internal_object(&coll, &olst); + if(_pop_list(olst.data.list, &val, s)) { + mb_check(mb_push_value(s, l, val)); + _MAKE_NIL(&ocoll); + _public_value_to_internal_object(&val, &ocoll); + _UNREF(&ocoll) + + _assign_public_value(&coll, 0); + } else { + mb_check(mb_push_value(s, l, val)); + + _assign_public_value(&coll, 0); + + _handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + } + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, val)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + return result; +} + +/* PEEK statement */ +static int _coll_peek(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + mb_value_t val; + _object_t olst; + _object_t* oval = 0; + _ls_node_t* node = 0; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(val); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_PEEK, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + if(coll.type != MB_DT_LIST) { + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + _MAKE_NIL(&olst); + _public_value_to_internal_object(&coll, &olst); + node = _ls_back(olst.data.list->list); + oval = node ? (_object_t*)node->data : 0; + if(oval) { + _internal_object_to_public_value(oval, &val); + + mb_check(mb_push_value(s, l, val)); + + _assign_public_value(&coll, 0); + } else { + mb_check(mb_push_value(s, l, val)); + + _assign_public_value(&coll, 0); + + _handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, DON2(l), MB_FUNC_WARNING, _exit, result); + } + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, val)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + return result; +} + +/* INSERT statement */ +static int _coll_insert(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + int_t idx = 0; + mb_value_t arg; + _object_t olst; + _object_t* oval = 0; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(arg); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_INSERT, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + mb_check(mb_pop_int(s, l, &idx)); + mb_check(mb_pop_value(s, l, &arg)); + + if(coll.type != MB_DT_LIST) { + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _MAKE_NIL(&olst); + _public_value_to_internal_object(&coll, &olst); + + if(!_insert_list(olst.data.list, idx, &arg, &oval)) { + _destroy_object(oval, 0); + + _handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + + mb_check(mb_push_value(s, l, coll)); + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* SORT statement */ +static int _coll_sort(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + _object_t olst; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_SORT, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + if(coll.type != MB_DT_LIST) { + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + _MAKE_NIL(&olst); + _public_value_to_internal_object(&coll, &olst); + + _sort_list(olst.data.list); + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* EXIST statement */ +static int _coll_exist(mb_interpreter_t* s, void** l){ + int result = MB_FUNC_OK; + mb_value_t coll; + mb_value_t arg; + _object_t ocoll; + mb_value_t ret; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(arg); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_EXIST, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + mb_check(mb_pop_value(s, l, &arg)); + + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + mb_make_bool(ret, _find_list(ocoll.data.list, &arg, 0)); + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + mb_make_bool(ret, _find_dict(ocoll.data.dict, &arg, 0)); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + mb_check(mb_push_value(s, l, ret)); + } else { + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* INDEX_OF statement */ +static int _coll_index_of(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + int idx = 0; + mb_value_t coll; + _object_t ocoll; + mb_value_t val; + mb_value_t ret; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(val); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_INDEX_OF, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + ret.type = MB_DT_UNKNOWN; + mb_check(mb_pop_value(s, l, &val)); + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + if(_find_list(ocoll.data.list, &val, &idx)) { + mb_make_int(ret, (int_t)idx); + } + + break; + default: + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, ret)); + } + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* REMOVE statement */ +static int _coll_remove(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + int_t idx = 0; + mb_value_t key; + _object_t ocoll; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(key); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_REMOVE, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + while(mb_has_arg(s, l)) { + mb_check(mb_pop_int(s, l, &idx)); + + if(!_remove_at_list(ocoll.data.list, idx)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + while(mb_has_arg(s, l)) { + mb_check(mb_pop_value(s, l, &key)); + + if(!_remove_dict(ocoll.data.dict, &key)) { + _handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } + } + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* CLEAR statement */ +static int _coll_clear(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + _object_t ocoll; + mb_meta_status_u os = MB_MS_NONE; + + mb_assert(s && l); + + mb_make_nil(coll); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + os = _try_overridden(s, l, &coll, _COLL_ID_CLEAR, MB_MF_COLL); + if((os & MB_MS_DONE) == MB_MS_NONE) { + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + _clear_list(ocoll.data.list); + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + _clear_dict(ocoll.data.dict); + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + } + + mb_check(mb_attempt_close_bracket(s, l)); + + if((os & MB_MS_RETURNED) == MB_MS_NONE) { + mb_check(mb_push_value(s, l, coll)); + } + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* CLONE statement */ +static int _coll_clone(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + _object_t ocoll; + _object_t otgt; + mb_value_t ret; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _MAKE_NIL(&ocoll); + _MAKE_NIL(&otgt); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + _clone_object(s, &ocoll, &otgt, false, true); + ret.type = MB_DT_LIST; + ret.value.list = otgt.data.list; + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + _clone_object(s, &ocoll, &otgt, false, true); + ret.type = MB_DT_DICT; + ret.value.dict = otgt.data.dict; + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* TO_ARRAY statement */ +static int _coll_to_array(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + _object_t ocoll; + _array_t* array = 0; + _array_helper_t helper; + mb_value_t ret; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + array = _create_array(s, mb_strdup("", 1), _DT_REAL); + array->count = ocoll.data.list->count; + array->dimension_count = 1; + array->dimensions[0] = ocoll.data.list->count; + _init_array(array); + helper.s = s; + helper.array = array; + helper.index = 0; + _LS_FOREACH(ocoll.data.list->list, _do_nothing_on_object, _copy_list_to_array, &helper); + ret.type = MB_DT_ARRAY; + ret.value.array = array; + + break; + default: + _handle_error_on_obj(s, SE_RN_LIST_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* ITERATOR statement */ +static int _coll_iterator(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t coll; + _object_t ocoll; + _list_it_t* lit = 0; + _dict_it_t* dit = 0; + mb_value_t ret; + + mb_assert(s && l); + + mb_make_nil(coll); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &coll)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _MAKE_NIL(&ocoll); + switch(coll.type) { + case MB_DT_LIST: + _public_value_to_internal_object(&coll, &ocoll); + lit = _create_list_it(ocoll.data.list, false); + ret.type = MB_DT_LIST_IT; + ret.value.list_it = lit; + + break; + case MB_DT_DICT: + _public_value_to_internal_object(&coll, &ocoll); + dit = _create_dict_it(ocoll.data.dict, false); + ret.type = MB_DT_DICT_IT; + ret.value.list_it = dit; + + break; + default: + _handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + _assign_public_value(&coll, 0); + + return result; +} + +/* MOVE_NEXT statement */ +static int _coll_move_next(mb_interpreter_t* s, void** l) { + int result = MB_FUNC_OK; + mb_value_t it; + _object_t oit; + mb_value_t ret; + + mb_assert(s && l); + + mb_make_nil(it); + mb_make_nil(ret); + + mb_check(mb_attempt_open_bracket(s, l)); + + mb_check(mb_pop_value(s, l, &it)); + + mb_check(mb_attempt_close_bracket(s, l)); + + _MAKE_NIL(&oit); + switch(it.type) { + case MB_DT_LIST_IT: + _public_value_to_internal_object(&it, &oit); + oit.data.list_it = _move_list_it_next(oit.data.list_it); + if(_invalid_list_it(oit.data.list_it)) { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } else if(oit.data.list_it) { + mb_make_bool(ret, true); + } else { + mb_make_nil(ret); + } + + break; + case MB_DT_DICT_IT: + _public_value_to_internal_object(&it, &oit); + oit.data.dict_it = _move_dict_it_next(oit.data.dict_it); + if(_invalid_dict_it(oit.data.dict_it)) { + _handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + } else if(oit.data.dict_it) { + mb_make_bool(ret, true); + } else { + mb_make_nil(ret); + } + + break; + default: + _handle_error_on_obj(s, SE_RN_ITERATOR_EXPECTED, s->source_file, DON2(l), MB_FUNC_ERR, _exit, result); + + break; + } + + mb_check(mb_push_value(s, l, ret)); + +_exit: + return result; +} +#endif /* MB_ENABLE_COLLECTION_LIB */ + +/* ========================================================} */ + +#ifdef MB_COMPACT_MODE +# pragma pack() +#endif /* MB_COMPACT_MODE */ + +#ifdef MB_CP_BORLANDC +# pragma warn .8004 +# pragma warn .8008 +# pragma warn .8012 +#endif /* MB_CP_BORLANDC */ + +#ifdef MB_CP_CLANG +# pragma clang diagnostic pop +#endif /* MB_CP_CLANG */ + +#ifdef MB_CP_VC +# pragma warning(pop) +#endif /* MB_CP_VC */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/core/my_basic.h b/core/my_basic.h index 6f46d4e..d44ab31 100755 --- a/core/my_basic.h +++ b/core/my_basic.h @@ -1,652 +1,652 @@ -/* -** This source file is part of MY-BASIC -** -** For the latest info, see https://github.com/paladin-t/my_basic/ -** -** Copyright (C) 2011 - 2016 Wang 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 */ - -#if defined _MSC_VER -# define MB_CP_VC _MSC_VER -#elif defined __clang__ -# define MB_CP_CLANG -#elif defined __CYGWIN__ -# define MB_CP_CYGWIN -#elif defined __MINGW32__ -# define MB_CP_MINGW32 -#elif defined __BORLANDC__ -# define MB_CP_BORLANDC -#elif defined __POCC__ -# define MB_CP_PELLESC -#elif defined __TINYC__ -# define MB_CP_TCC -#elif defined __GNUC__ || defined __GNUG__ -# define MB_CP_GCC -#elif defined __ICC || defined __INTEL_COMPILER -# define MB_CP_ICC -#elif defined __HP_cc || defined __HP_aCC -# define MB_CP_HPC -#elif defined __IBMC__ || defined __IBMCPP__ -# define MB_CP_IBMC -#elif defined __PGI -# define MB_CP_PGCC -#elif defined __SUNPRO_C || defined__SUNPRO_CC -# define MB_CP_SOLARISC -#elif defined ARDUINO -# define MB_CP_ARDUINO -#else -# define MB_CP_UNKNOWN -#endif /* Compiler dependent macro */ - -#if defined _WIN64 -# define MB_OS_WIN -# define MB_OS_WIN64 -#elif defined _WIN32 -# define MB_OS_WIN -# define MB_OS_WIN32 -#elif defined __APPLE__ -# include -# define MB_OS_APPLE -# ifdef TARGET_OS_IPHONE -# define MB_OS_IOS -# elif defined TARGET_IPHONE_SIMULATOR -# define MB_OS_IOS_SIM -# elif defined TARGET_OS_MAC -# define MB_OS_MAC -# endif -#elif defined __unix__ -# define MB_OS_UNIX -#elif defined __linux__ -# define MB_OS_LINUX -#elif defined __ANDROID__ -# define MB_OS_ANDROID -#else -# define MB_OS_UNKNOWN -#endif /* OS dependent macro */ - -#ifndef MBAPI -# define MBAPI -#endif /* MBAPI */ - -#ifndef MB_SIMPLE_ARRAY -# define MB_SIMPLE_ARRAY -#endif /* MB_SIMPLE_ARRAY */ - -#ifndef MB_ENABLE_ARRAY_REF -# define MB_ENABLE_ARRAY_REF -#endif /* MB_ENABLE_ARRAY_REF */ - -#ifndef MB_MAX_DIMENSION_COUNT -# define MB_MAX_DIMENSION_COUNT 4 -#endif /* MB_MAX_DIMENSION_COUNT */ - -#ifndef MB_ENABLE_COLLECTION_LIB -# define MB_ENABLE_COLLECTION_LIB -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifndef MB_ENABLE_USERTYPE_REF -# define MB_ENABLE_USERTYPE_REF -#endif /* MB_ENABLE_USERTYPE_REF */ - -#ifndef MB_ENABLE_CLASS -# define MB_ENABLE_CLASS -#endif /* MB_ENABLE_CLASS */ - -#ifndef MB_ENABLE_LAMBDA -# define MB_ENABLE_LAMBDA -#endif /* MB_ENABLE_LAMBDA */ - -#ifndef MB_ENABLE_MODULE -# define MB_ENABLE_MODULE -#endif /* MB_ENABLE_MODULE */ - -#ifndef MB_ENABLE_UNICODE -# define MB_ENABLE_UNICODE -#endif /* MB_ENABLE_UNICODE */ - -#ifndef MB_ENABLE_UNICODE_ID -# define MB_ENABLE_UNICODE_ID -# if defined MB_ENABLE_UNICODE_ID && !defined MB_ENABLE_UNICODE -# error "Requires MB_ENABLE_UNICODE enabled." -# endif -#endif /* MB_ENABLE_UNICODE_ID */ - -#ifndef MB_GC_GARBAGE_THRESHOLD -# define MB_GC_GARBAGE_THRESHOLD 16 -#endif /* MB_GC_GARBAGE_THRESHOLD */ - -#ifndef MB_ENABLE_ALLOC_STAT -# define MB_ENABLE_ALLOC_STAT -#endif /* MB_ENABLE_ALLOC_STAT */ - -#ifndef MB_ENABLE_SOURCE_TRACE -# define MB_ENABLE_SOURCE_TRACE -#endif /* MB_ENABLE_SOURCE_TRACE */ - -#ifndef MB_ENABLE_STACK_TRACE -# define MB_ENABLE_STACK_TRACE -#endif /* MB_ENABLE_STACK_TRACE */ - -#ifndef MB_CONVERT_TO_INT_LEVEL_NONE -# define MB_CONVERT_TO_INT_LEVEL_NONE 0 -#endif /* MB_CONVERT_TO_INT_LEVEL_NONE */ -#ifndef MB_CONVERT_TO_INT_LEVEL_ALL -# define MB_CONVERT_TO_INT_LEVEL_ALL 1 -#endif /* MB_CONVERT_TO_INT_LEVEL_ALL */ - -#ifndef MB_CONVERT_TO_INT_LEVEL -# define MB_CONVERT_TO_INT_LEVEL MB_CONVERT_TO_INT_LEVEL_ALL -#endif /* MB_CONVERT_TO_INT_LEVEL */ - -#ifndef MB_COMPACT_MODE -# define MB_COMPACT_MODE -#endif /* MB_COMPACT_MODE */ - -#ifdef MB_COMPACT_MODE -# pragma pack(1) -#endif /* MB_COMPACT_MODE */ - -#ifndef __cplusplus -# ifndef true -# define true (!0) -# endif /* true */ -# ifndef false -# define false (0) -# endif /* false */ -#endif /* __cplusplus */ - -#ifndef bool_t -# define bool_t char -#endif /* bool_t */ -#ifndef int_t -# define int_t int -#endif /* int_t */ -#ifndef real_t -# ifdef MB_DOUBLE_FLOAT -# define real_t double -# else /* MB_DOUBLE_FLOAT */ -# define real_t float -# endif /* MB_DOUBLE_FLOAT */ -#endif /* real_t */ - -#ifndef mb_strtol -# define mb_strtol(__s, __e, __r) strtol((__s), (__e), (__r)) -#endif /* mb_strtol */ -#ifndef mb_strtod -# define mb_strtod(__s, __e) strtod((__s), (__e)) -#endif /* mb_strtod */ - -#ifndef MB_INT_FMT -# define MB_INT_FMT "%d" -#endif /* MB_INT_FMT */ -#ifndef MB_REAL_FMT -# define MB_REAL_FMT "%g" -#endif /* MB_REAL_FMT */ - -#ifndef MB_FNAN -# ifdef MB_DOUBLE_FLOAT -# define MB_FNAN 0x7FF8000000000000 -# else /* MB_DOUBLE_FLOAT */ -# define MB_FNAN 0x7FC00000 -# endif /* MB_DOUBLE_FLOAT */ -#endif /* MB_FNAN */ -#ifndef MB_FINF -# ifdef MB_DOUBLE_FLOAT -# define MB_FINF 0x7FF0000000000000 -# else /* MB_DOUBLE_FLOAT */ -# define MB_FINF 0x7F800000 -# endif /* MB_DOUBLE_FLOAT */ -#endif /* MB_FINF */ - -#ifndef MB_EOS -# define MB_EOS '\n' -#endif /* MB_EOS */ -#ifndef MB_NIL -# define MB_NIL "NIL" -#endif /* MB_NIL */ -#ifndef MB_TRUE -# define MB_TRUE "TRUE" -#endif /* MB_TRUE */ -#ifndef MB_FALSE -# define MB_FALSE "FALSE" -#endif /* MB_FALSE */ -#ifndef MB_NULL_STRING -# define MB_NULL_STRING "(EMPTY)" -#endif /* MB_NULL_STRING */ - -#ifndef mb_max -# define mb_max(a, b) (((a) > (b)) ? (a) : (b)) -#endif /* mb_max */ - -#ifndef mb_stricmp -# ifdef MB_CP_VC -# define mb_stricmp _strcmpi -# else /* MB_CP_VC */ -# ifdef MB_CP_BORLANDC -# define mb_stricmp stricmp -# elif defined MB_CP_PELLESC -# define mb_stricmp _stricmp -# else -# define mb_stricmp strcasecmp -# endif -# endif /* MB_CP_VC */ -#endif /* mb_stricmp */ - -#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_make_nil -# define mb_make_nil(__v) do { (__v).value.integer = 0; (__v).type = MB_DT_NIL; } while(0) -#endif /* mb_make_nil */ -#ifndef mb_make_type -# define mb_make_type(__v, __d) do { (__v).value.type = (__d); (__v).type = MB_DT_TYPE; } while(0) -#endif /* mb_make_type */ -#ifndef mb_make_bool -# define mb_make_bool(__v, __d) do { (__v).value.integer = (!!(__d) ? true : false); (__v).type = MB_DT_INT; } while(0) -#endif /* mb_make_bool */ -#ifndef mb_make_int -# define mb_make_int(__v, __d) do { (__v).value.integer = (__d); (__v).type = MB_DT_INT; } while(0) -#endif /* mb_make_int */ -#ifndef mb_make_real -# define mb_make_real(__v, __d) do { (__v).value.float_point = (__d); (__v).type = MB_DT_REAL; } while(0) -#endif /* mb_make_real */ -#ifndef mb_make_string -# define mb_make_string(__v, __d) do { (__v).value.string = (__d); (__v).type = MB_DT_STRING; } while(0) -#endif /* mb_make_string */ -#ifndef mb_make_usertype -# define mb_make_usertype(__v, __d) do { (__v).value.usertype = (__d); (__v).type = MB_DT_USERTYPE; } while(0) -#endif /* mb_make_usertype */ -#ifdef MB_ENABLE_USERTYPE_REF -# ifndef mb_make_usertype_ref -# define mb_make_usertype_ref(__v, __d) do { (__v).value.usertype_ref = (__d); (__v).type = MB_DT_USERTYPE_REF; } while(0) -# endif /* mb_make_usertype_ref */ -#endif /* MB_ENABLE_USERTYPE_REF */ -#ifndef mb_make_array -# define mb_make_array(__v, __d) do { (__v).value.array = (__d); (__v).type = MB_DT_ARRAY; } while(0) -#endif /* mb_make_array */ -#ifdef MB_ENABLE_COLLECTION_LIB -# ifndef mb_make_list -# define mb_make_list(__v, __d) do { (__v).value.list = (__d); (__v).type = MB_DT_LIST; } while(0) -# endif /* mb_make_list */ -# ifndef mb_make_dict -# define mb_make_dict(__v, __d) do { (__v).value.dict = (__d); (__v).type = MB_DT_DICT; } while(0) -# endif /* mb_make_dict */ -#endif /* MB_ENABLE_COLLECTION_LIB */ - -#ifndef mb_int_val -# define mb_int_val(__v, __d) do { if((__v).type == MB_DT_INT) (__d) = (__v).value.integer; else if((__v).type == MB_DT_REAL) (__d) = (int_t)((__v).value.float_point); else (__d) = ~((int_t)0); } while(0) -#endif /* mb_int_val */ - -#ifndef MB_CODES -# define MB_CODES -# define MB_FUNC_OK 0 -# define MB_FUNC_IGNORE 1 -# define MB_FUNC_WARNING 2 -# define MB_FUNC_ERR 3 -# define MB_FUNC_BYE 4 -# define MB_FUNC_SUSPEND 5 -# define MB_FUNC_END 6 -# define MB_LOOP_BREAK 101 -# define MB_LOOP_CONTINUE 102 -# define MB_SUB_RETURN 103 -# define MB_EXTENDED_ABORT 1001 -#endif /* MB_CODES */ - -#ifndef mb_check -# define mb_check(__r) do { int __hr = __r; if(__hr != MB_FUNC_OK) { return __hr; } } while(0) -#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 */ - -#ifndef mb_convert_to_int_if_posible -# if MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE -# define mb_convert_to_int_if_posible(__v) ((void)(__v)) -# else /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ -# define mb_convert_to_int_if_posible(__v) \ - do { \ - if((__v).type == MB_DT_REAL && (real_t)(int_t)(__v).value.float_point == (__v).value.float_point) { \ - (__v).type = MB_DT_INT; \ - (__v).value.integer = (int_t)(__v).value.float_point; \ - } \ - } while(0) -# endif /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ -#endif /* mb_convert_to_int_if_posible */ - -struct mb_interpreter_t; - -typedef enum mb_error_e { - SE_NO_ERR = 0, - /** Common */ - SE_CM_OPEN_MB_FAILED, - SE_CM_FUNC_EXISTS, - SE_CM_FUNC_NOT_EXISTS, - SE_CM_NOT_SUPPORTED, - /** Parsing */ - SE_PS_OPEN_FILE_FAILED, - SE_PS_SYMBOL_TOO_LONG, - SE_PS_INVALID_CHAR, - /** Running */ - SE_RN_EMPTY_PROGRAM, - SE_RN_PROGRAM_TOO_LONG, - SE_RN_SYNTAX_ERROR, - SE_RN_INVALID_DATA_TYPE, - SE_RN_TYPE_NOT_MATCH, - SE_RN_NUMBER_OVERFLOW, - SE_RN_INVALID_STRING, - SE_RN_INDEX_OUT_OF_BOUND, - SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, - SE_RN_ILLEGAL_BOUND, - SE_RN_TOO_MANY_DIMENSIONS, - SE_RN_OPERATION_FAILED, - SE_RN_INVALID_OPERATION_USAGE, - SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, - SE_RN_RANK_OUT_OF_BOUND, - SE_RN_NEED_COMPLEX_ARRAY, - SE_RN_LABEL_NOT_EXISTS, - SE_RN_NO_RETURN_POINT, - SE_RN_COLON_EXPECTED, - SE_RN_COMMA_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_NESTED_TOO_DEEP, - SE_RN_INCOMPLETE_STRUCTURE, - SE_RN_FUNCTION_EXPECTED, - SE_RN_VAR_OR_ARRAY_EXPECTED, - SE_RN_ASSIGN_OPERATOR_EXPECTED, - SE_RN_STRING_EXPECTED, - SE_RN_NUMBER_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_DUPLICATE_ID, - 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, - SE_RN_MODULE_NOT_MATCH, - SE_RN_WRONG_FUNCTION_REACHED, - SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, - SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, - SE_RN_INVALID_ROUTINE, - SE_RN_INCOMPLETE_ROUTINE, - SE_RN_ROUTINE_EXPECTED, - SE_RN_DUPLICATE_ROUTINE, - SE_RN_TOO_MANY_ROUTINES, - SE_RN_INVALID_CLASS, - SE_RN_INCOMPLETE_CLASS, - SE_RN_CLASS_EXPECTED, - SE_RN_DUPLICATE_CLASS, - SE_RN_WRONG_META_CLASS, - SE_RN_HASH_AND_COMPARE_MUST_COME_TOGETHER, - SE_RN_CANNOT_CHANGE_ME, - SE_RN_INVALID_LAMBDA, - SE_RN_LIST_EXPECTED, - SE_RN_COLLECTION_EXPECTED, - SE_RN_ITERATOR_EXPECTED, - SE_RN_COLLECTION_OR_ITERATOR_EXPECTED, - SE_RN_COLLECTION_OR_ITERATOR_OR_CLASS_EXPECTED, - SE_RN_INVALID_ITERATOR, - SE_RN_EMPTY_COLLECTION, - SE_RN_REFERENCED_TYPE_EXPECTED, - SE_RN_REFERENCE_COUNT_OVERFLOW, - SE_RN_WEAK_REFERENCE_COUNT_OVERFLOW, - SE_RN_DEBUG_ID_NOT_FOUND, - SE_RN_STACK_TRACE_DISABLED, - /** Extended abort */ - SE_EA_EXTENDED_ABORT, - /** Extra */ - SE_COUNT -} mb_error_e; - -typedef enum mb_data_e { - MB_DT_NIL = 0, - MB_DT_UNKNOWN = 1 << 0, - MB_DT_INT = 1 << 1, - MB_DT_REAL = 1 << 2, - MB_DT_NUM = MB_DT_INT | MB_DT_REAL, - MB_DT_STRING = 1 << 3, - MB_DT_TYPE = 1 << 4, - MB_DT_USERTYPE = 1 << 5, -#ifdef MB_ENABLE_USERTYPE_REF - MB_DT_USERTYPE_REF = 1 << 6, -#endif /* MB_ENABLE_USERTYPE_REF */ - MB_DT_ARRAY = 1 << 7, -#ifdef MB_ENABLE_COLLECTION_LIB - MB_DT_LIST = 1 << 8, - MB_DT_LIST_IT = 1 << 9, - MB_DT_DICT = 1 << 10, - MB_DT_DICT_IT = 1 << 11, - MB_DT_COLLECTION = MB_DT_LIST | MB_DT_DICT, - MB_DT_ITERATOR = MB_DT_LIST_IT | MB_DT_DICT_IT, -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - MB_DT_CLASS = 1 << 12, -#endif /* MB_ENABLE_CLASS */ - MB_DT_ROUTINE = 1 << 13 -} mb_data_e; - -typedef enum mb_meta_func_u { - MB_MF_IS = 1 << 0, - MB_MF_ADD = 1 << 1, - MB_MF_SUB = 1 << 2, - MB_MF_MUL = 1 << 3, - MB_MF_DIV = 1 << 4, - MB_MF_NEG = 1 << 5, - MB_MF_CALC = MB_MF_IS | MB_MF_ADD | MB_MF_SUB | MB_MF_MUL | MB_MF_DIV | MB_MF_NEG, - MB_MF_COLL = 1 << 6, - MB_MF_FUNC = 1 << 7 -} mb_meta_func_u; - -typedef enum mb_meta_status_u { - MB_MS_NONE = 0, - MB_MS_DONE = 1 << 0, - MB_MS_RETURNED = 1 << 1 -} mb_meta_status_u; - -typedef unsigned char mb_val_bytes_t[mb_max(mb_max(sizeof(void*), sizeof(unsigned long)), mb_max(sizeof(int_t), sizeof(real_t)))]; - -typedef union mb_value_u { - int_t integer; - real_t float_point; - char* string; - mb_data_e type; - void* usertype; -#ifdef MB_ENABLE_USERTYPE_REF - void* usertype_ref; -#endif /* MB_ENABLE_USERTYPE_REF */ - void* array; -#ifdef MB_ENABLE_COLLECTION_LIB - void* list; - void* list_it; - void* dict; - void* dict_it; -#endif /* MB_ENABLE_COLLECTION_LIB */ -#ifdef MB_ENABLE_CLASS - void* instance; -#endif /* MB_ENABLE_CLASS */ - void* routine; - mb_val_bytes_t bytes; -} mb_value_u; - -mb_static_assert(sizeof(mb_val_bytes_t) >= sizeof(mb_value_u)); - -typedef struct mb_value_t { - mb_data_e type; - mb_value_u value; -} mb_value_t; - -typedef unsigned short mb_mem_tag_t; - -typedef int (* mb_func_t)(struct mb_interpreter_t*, void**); -typedef int (* mb_has_routine_arg_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, unsigned*, void*); -typedef int (* mb_pop_routine_arg_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, unsigned*, void*, mb_value_t*); -typedef int (* mb_routine_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, void*, mb_has_routine_arg_func_t, mb_pop_routine_arg_func_t); -typedef void (* mb_debug_stepped_handler_t)(struct mb_interpreter_t*, void**, char*, int, unsigned short, unsigned short); -typedef void (* mb_error_handler_t)(struct mb_interpreter_t*, enum mb_error_e, char*, char*, int, unsigned short, unsigned short, int); -typedef int (* mb_print_func_t)(const char*, ...); -typedef int (* mb_input_func_t)(char*, int); -typedef int (* mb_import_handler_t)(struct mb_interpreter_t*, const char*); -typedef void (* mb_dtor_func_t)(struct mb_interpreter_t*, void*); -typedef void* (* mb_clone_func_t)(struct mb_interpreter_t*, void*); -typedef unsigned (* mb_hash_func_t)(struct mb_interpreter_t*, void*); -typedef int (* mb_cmp_func_t)(struct mb_interpreter_t*, void*, void*); -typedef int (* mb_fmt_func_t)(struct mb_interpreter_t*, void*, char*, unsigned); -typedef int (* mb_meta_operator_t)(struct mb_interpreter_t*, void**, mb_value_t*, mb_value_t*, mb_value_t*); -typedef mb_meta_status_u (* mb_meta_func_t)(struct mb_interpreter_t*, void**, const char*); -typedef char* (* mb_memory_allocate_func_t)(unsigned); -typedef void (* mb_memory_free_func_t)(char*); - -MBAPI unsigned mb_ver(void); -MBAPI const char* mb_ver_string(void); - -MBAPI int mb_init(void); -MBAPI int mb_dispose(void); -MBAPI int mb_open(struct mb_interpreter_t** s); -MBAPI int mb_close(struct mb_interpreter_t** s); -MBAPI int mb_reset(struct mb_interpreter_t** s, bool_t clrf/* = false*/); - -MBAPI int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f); -MBAPI int mb_remove_func(struct mb_interpreter_t* s, const char* n); -MBAPI int mb_remove_reserved_func(struct mb_interpreter_t* s, const char* n); -MBAPI int mb_begin_module(struct mb_interpreter_t* s, const char* n); -MBAPI int mb_end_module(struct mb_interpreter_t* s); - -MBAPI int mb_attempt_func_begin(struct mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_func_end(struct mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_open_bracket(struct mb_interpreter_t* s, void** l); -MBAPI int mb_attempt_close_bracket(struct mb_interpreter_t* s, void** l); -MBAPI int mb_has_arg(struct mb_interpreter_t* s, void** l); -MBAPI int mb_pop_int(struct mb_interpreter_t* s, void** l, int_t* val); -MBAPI int mb_pop_real(struct mb_interpreter_t* s, void** l, real_t* val); -MBAPI int mb_pop_string(struct mb_interpreter_t* s, void** l, char** val); -MBAPI int mb_pop_usertype(struct mb_interpreter_t* s, void** l, void** val); -MBAPI int mb_pop_value(struct mb_interpreter_t* s, void** l, mb_value_t* val); -MBAPI int mb_push_int(struct mb_interpreter_t* s, void** l, int_t val); -MBAPI int mb_push_real(struct mb_interpreter_t* s, void** l, real_t val); -MBAPI int mb_push_string(struct mb_interpreter_t* s, void** l, char* val); -MBAPI int mb_push_usertype(struct mb_interpreter_t* s, void** l, void* val); -MBAPI int mb_push_value(struct mb_interpreter_t* s, void** l, mb_value_t val); - -MBAPI int mb_begin_class(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t** meta, int c, mb_value_t* out); -MBAPI int mb_end_class(struct mb_interpreter_t* s, void** l); -MBAPI int mb_get_class_userdata(struct mb_interpreter_t* s, void** l, void** d); -MBAPI int mb_set_class_userdata(struct mb_interpreter_t* s, void** l, void* d); - -MBAPI int mb_get_value_by_name(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val); -MBAPI int mb_add_var(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t val, bool_t force); -MBAPI int mb_get_var(struct mb_interpreter_t* s, void** l, void** v); -MBAPI int mb_get_var_value(struct mb_interpreter_t* s, void* v, mb_value_t* val); -MBAPI int mb_set_var_value(struct mb_interpreter_t* s, void* v, mb_value_t val); -MBAPI int mb_init_array(struct mb_interpreter_t* s, void** l, mb_data_e t, int* d, int c, void** a); -MBAPI int mb_get_array_len(struct mb_interpreter_t* s, void** l, void* a, int r, int* i); -MBAPI int mb_get_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t* val); -MBAPI int mb_set_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t val); -MBAPI int mb_init_coll(struct mb_interpreter_t* s, void** l, mb_value_t* coll); -MBAPI int mb_get_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t* val); -MBAPI int mb_set_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t val); -MBAPI int mb_remove_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx); -MBAPI int mb_count_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, int* c); -MBAPI int mb_make_ref_value(struct mb_interpreter_t* s, void* val, mb_value_t* out, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs/* = NULL*/, mb_cmp_func_t cp/* = NULL*/, mb_fmt_func_t ft/* = NULL*/); -MBAPI int mb_get_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val, void** out); -MBAPI int mb_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val); -MBAPI int mb_unref_value(struct mb_interpreter_t* s, void** l, mb_value_t val); -MBAPI int mb_override_value(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_meta_func_u m, void* f); -MBAPI int mb_dispose_value(struct mb_interpreter_t* s, mb_value_t val); - -MBAPI int mb_get_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val); -MBAPI int mb_set_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_routine_func_t f, bool_t force); -MBAPI int mb_eval_routine(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_value_t* args, unsigned argc); - -MBAPI int mb_load_string(struct mb_interpreter_t* s, const char* l, bool_t reset/* = true*/); -MBAPI int mb_load_file(struct mb_interpreter_t* s, const char* f); -MBAPI int mb_run(struct mb_interpreter_t* s); -MBAPI int mb_suspend(struct mb_interpreter_t* s, void** l); -MBAPI int mb_schedule_suspend(struct mb_interpreter_t* s, int t); - -MBAPI int mb_debug_get(struct mb_interpreter_t* s, const char* n, mb_value_t* val); -MBAPI int mb_debug_set(struct mb_interpreter_t* s, const char* n, mb_value_t val); -MBAPI int mb_debug_get_stack_trace(struct mb_interpreter_t* s, void** l, char** fs, unsigned fc); -MBAPI int mb_debug_set_stepped_handler(struct mb_interpreter_t* s, mb_debug_stepped_handler_t h); - -MBAPI const char* mb_get_type_string(mb_data_e t); - -MBAPI int mb_raise_error(struct mb_interpreter_t* s, void** l, mb_error_e err, int ret); -MBAPI mb_error_e mb_get_last_error(struct mb_interpreter_t* s); -MBAPI const char* mb_get_error_desc(mb_error_e err); -MBAPI int mb_set_error_handler(struct mb_interpreter_t* s, mb_error_handler_t h); - -MBAPI int mb_set_printer(struct mb_interpreter_t* s, mb_print_func_t p); -MBAPI int mb_set_inputer(struct mb_interpreter_t* s, mb_input_func_t p); - -MBAPI int mb_gc(struct mb_interpreter_t* s, int_t* collected/* = NULL*/); -MBAPI int mb_get_userdata(struct mb_interpreter_t* s, void** d); -MBAPI int mb_set_userdata(struct mb_interpreter_t* s, void* d); -MBAPI int mb_set_import_handler(struct mb_interpreter_t* s, mb_import_handler_t h); -MBAPI int mb_gets(char* buf, int s); -MBAPI char* mb_memdup(const char* val, unsigned size); -MBAPI int mb_set_memory_manager(mb_memory_allocate_func_t a, mb_memory_free_func_t f); - -#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 - 2016 Wang 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 */ + +#if defined _MSC_VER +# define MB_CP_VC _MSC_VER +#elif defined __clang__ +# define MB_CP_CLANG +#elif defined __CYGWIN__ +# define MB_CP_CYGWIN +#elif defined __MINGW32__ +# define MB_CP_MINGW32 +#elif defined __BORLANDC__ +# define MB_CP_BORLANDC +#elif defined __POCC__ +# define MB_CP_PELLESC +#elif defined __TINYC__ +# define MB_CP_TCC +#elif defined __GNUC__ || defined __GNUG__ +# define MB_CP_GCC +#elif defined __ICC || defined __INTEL_COMPILER +# define MB_CP_ICC +#elif defined __HP_cc || defined __HP_aCC +# define MB_CP_HPC +#elif defined __IBMC__ || defined __IBMCPP__ +# define MB_CP_IBMC +#elif defined __PGI +# define MB_CP_PGCC +#elif defined __SUNPRO_C || defined__SUNPRO_CC +# define MB_CP_SOLARISC +#elif defined ARDUINO +# define MB_CP_ARDUINO +#else +# define MB_CP_UNKNOWN +#endif /* Compiler dependent macro */ + +#if defined _WIN64 +# define MB_OS_WIN +# define MB_OS_WIN64 +#elif defined _WIN32 +# define MB_OS_WIN +# define MB_OS_WIN32 +#elif defined __APPLE__ +# include +# define MB_OS_APPLE +# ifdef TARGET_OS_IPHONE +# define MB_OS_IOS +# elif defined TARGET_IPHONE_SIMULATOR +# define MB_OS_IOS_SIM +# elif defined TARGET_OS_MAC +# define MB_OS_MAC +# endif +#elif defined __unix__ +# define MB_OS_UNIX +#elif defined __linux__ +# define MB_OS_LINUX +#elif defined __ANDROID__ +# define MB_OS_ANDROID +#else +# define MB_OS_UNKNOWN +#endif /* OS dependent macro */ + +#ifndef MBAPI +# define MBAPI +#endif /* MBAPI */ + +#ifndef MB_SIMPLE_ARRAY +# define MB_SIMPLE_ARRAY +#endif /* MB_SIMPLE_ARRAY */ + +#ifndef MB_ENABLE_ARRAY_REF +# define MB_ENABLE_ARRAY_REF +#endif /* MB_ENABLE_ARRAY_REF */ + +#ifndef MB_MAX_DIMENSION_COUNT +# define MB_MAX_DIMENSION_COUNT 4 +#endif /* MB_MAX_DIMENSION_COUNT */ + +#ifndef MB_ENABLE_COLLECTION_LIB +# define MB_ENABLE_COLLECTION_LIB +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifndef MB_ENABLE_USERTYPE_REF +# define MB_ENABLE_USERTYPE_REF +#endif /* MB_ENABLE_USERTYPE_REF */ + +#ifndef MB_ENABLE_CLASS +# define MB_ENABLE_CLASS +#endif /* MB_ENABLE_CLASS */ + +#ifndef MB_ENABLE_LAMBDA +# define MB_ENABLE_LAMBDA +#endif /* MB_ENABLE_LAMBDA */ + +#ifndef MB_ENABLE_MODULE +# define MB_ENABLE_MODULE +#endif /* MB_ENABLE_MODULE */ + +#ifndef MB_ENABLE_UNICODE +# define MB_ENABLE_UNICODE +#endif /* MB_ENABLE_UNICODE */ + +#ifndef MB_ENABLE_UNICODE_ID +# define MB_ENABLE_UNICODE_ID +# if defined MB_ENABLE_UNICODE_ID && !defined MB_ENABLE_UNICODE +# error "Requires MB_ENABLE_UNICODE enabled." +# endif +#endif /* MB_ENABLE_UNICODE_ID */ + +#ifndef MB_GC_GARBAGE_THRESHOLD +# define MB_GC_GARBAGE_THRESHOLD 16 +#endif /* MB_GC_GARBAGE_THRESHOLD */ + +#ifndef MB_ENABLE_ALLOC_STAT +# define MB_ENABLE_ALLOC_STAT +#endif /* MB_ENABLE_ALLOC_STAT */ + +#ifndef MB_ENABLE_SOURCE_TRACE +# define MB_ENABLE_SOURCE_TRACE +#endif /* MB_ENABLE_SOURCE_TRACE */ + +#ifndef MB_ENABLE_STACK_TRACE +# define MB_ENABLE_STACK_TRACE +#endif /* MB_ENABLE_STACK_TRACE */ + +#ifndef MB_CONVERT_TO_INT_LEVEL_NONE +# define MB_CONVERT_TO_INT_LEVEL_NONE 0 +#endif /* MB_CONVERT_TO_INT_LEVEL_NONE */ +#ifndef MB_CONVERT_TO_INT_LEVEL_ALL +# define MB_CONVERT_TO_INT_LEVEL_ALL 1 +#endif /* MB_CONVERT_TO_INT_LEVEL_ALL */ + +#ifndef MB_CONVERT_TO_INT_LEVEL +# define MB_CONVERT_TO_INT_LEVEL MB_CONVERT_TO_INT_LEVEL_ALL +#endif /* MB_CONVERT_TO_INT_LEVEL */ + +#ifndef MB_COMPACT_MODE +# define MB_COMPACT_MODE +#endif /* MB_COMPACT_MODE */ + +#ifdef MB_COMPACT_MODE +# pragma pack(1) +#endif /* MB_COMPACT_MODE */ + +#ifndef __cplusplus +# ifndef true +# define true (!0) +# endif /* true */ +# ifndef false +# define false (0) +# endif /* false */ +#endif /* __cplusplus */ + +#ifndef bool_t +# define bool_t char +#endif /* bool_t */ +#ifndef int_t +# define int_t int +#endif /* int_t */ +#ifndef real_t +# ifdef MB_DOUBLE_FLOAT +# define real_t double +# else /* MB_DOUBLE_FLOAT */ +# define real_t float +# endif /* MB_DOUBLE_FLOAT */ +#endif /* real_t */ + +#ifndef mb_strtol +# define mb_strtol(__s, __e, __r) strtol((__s), (__e), (__r)) +#endif /* mb_strtol */ +#ifndef mb_strtod +# define mb_strtod(__s, __e) strtod((__s), (__e)) +#endif /* mb_strtod */ + +#ifndef MB_INT_FMT +# define MB_INT_FMT "%d" +#endif /* MB_INT_FMT */ +#ifndef MB_REAL_FMT +# define MB_REAL_FMT "%g" +#endif /* MB_REAL_FMT */ + +#ifndef MB_FNAN +# ifdef MB_DOUBLE_FLOAT +# define MB_FNAN 0x7FF8000000000000 +# else /* MB_DOUBLE_FLOAT */ +# define MB_FNAN 0x7FC00000 +# endif /* MB_DOUBLE_FLOAT */ +#endif /* MB_FNAN */ +#ifndef MB_FINF +# ifdef MB_DOUBLE_FLOAT +# define MB_FINF 0x7FF0000000000000 +# else /* MB_DOUBLE_FLOAT */ +# define MB_FINF 0x7F800000 +# endif /* MB_DOUBLE_FLOAT */ +#endif /* MB_FINF */ + +#ifndef MB_EOS +# define MB_EOS '\n' +#endif /* MB_EOS */ +#ifndef MB_NIL +# define MB_NIL "NIL" +#endif /* MB_NIL */ +#ifndef MB_TRUE +# define MB_TRUE "TRUE" +#endif /* MB_TRUE */ +#ifndef MB_FALSE +# define MB_FALSE "FALSE" +#endif /* MB_FALSE */ +#ifndef MB_NULL_STRING +# define MB_NULL_STRING "(EMPTY)" +#endif /* MB_NULL_STRING */ + +#ifndef mb_max +# define mb_max(a, b) (((a) > (b)) ? (a) : (b)) +#endif /* mb_max */ + +#ifndef mb_stricmp +# ifdef MB_CP_VC +# define mb_stricmp _strcmpi +# else /* MB_CP_VC */ +# ifdef MB_CP_BORLANDC +# define mb_stricmp stricmp +# elif defined MB_CP_PELLESC +# define mb_stricmp _stricmp +# else +# define mb_stricmp strcasecmp +# endif +# endif /* MB_CP_VC */ +#endif /* mb_stricmp */ + +#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_make_nil +# define mb_make_nil(__v) do { (__v).value.integer = 0; (__v).type = MB_DT_NIL; } while(0) +#endif /* mb_make_nil */ +#ifndef mb_make_type +# define mb_make_type(__v, __d) do { (__v).value.type = (__d); (__v).type = MB_DT_TYPE; } while(0) +#endif /* mb_make_type */ +#ifndef mb_make_bool +# define mb_make_bool(__v, __d) do { (__v).value.integer = (!!(__d) ? true : false); (__v).type = MB_DT_INT; } while(0) +#endif /* mb_make_bool */ +#ifndef mb_make_int +# define mb_make_int(__v, __d) do { (__v).value.integer = (__d); (__v).type = MB_DT_INT; } while(0) +#endif /* mb_make_int */ +#ifndef mb_make_real +# define mb_make_real(__v, __d) do { (__v).value.float_point = (__d); (__v).type = MB_DT_REAL; } while(0) +#endif /* mb_make_real */ +#ifndef mb_make_string +# define mb_make_string(__v, __d) do { (__v).value.string = (__d); (__v).type = MB_DT_STRING; } while(0) +#endif /* mb_make_string */ +#ifndef mb_make_usertype +# define mb_make_usertype(__v, __d) do { (__v).value.usertype = (__d); (__v).type = MB_DT_USERTYPE; } while(0) +#endif /* mb_make_usertype */ +#ifdef MB_ENABLE_USERTYPE_REF +# ifndef mb_make_usertype_ref +# define mb_make_usertype_ref(__v, __d) do { (__v).value.usertype_ref = (__d); (__v).type = MB_DT_USERTYPE_REF; } while(0) +# endif /* mb_make_usertype_ref */ +#endif /* MB_ENABLE_USERTYPE_REF */ +#ifndef mb_make_array +# define mb_make_array(__v, __d) do { (__v).value.array = (__d); (__v).type = MB_DT_ARRAY; } while(0) +#endif /* mb_make_array */ +#ifdef MB_ENABLE_COLLECTION_LIB +# ifndef mb_make_list +# define mb_make_list(__v, __d) do { (__v).value.list = (__d); (__v).type = MB_DT_LIST; } while(0) +# endif /* mb_make_list */ +# ifndef mb_make_dict +# define mb_make_dict(__v, __d) do { (__v).value.dict = (__d); (__v).type = MB_DT_DICT; } while(0) +# endif /* mb_make_dict */ +#endif /* MB_ENABLE_COLLECTION_LIB */ + +#ifndef mb_int_val +# define mb_int_val(__v, __d) do { if((__v).type == MB_DT_INT) (__d) = (__v).value.integer; else if((__v).type == MB_DT_REAL) (__d) = (int_t)((__v).value.float_point); else (__d) = ~((int_t)0); } while(0) +#endif /* mb_int_val */ + +#ifndef MB_CODES +# define MB_CODES +# define MB_FUNC_OK 0 +# define MB_FUNC_IGNORE 1 +# define MB_FUNC_WARNING 2 +# define MB_FUNC_ERR 3 +# define MB_FUNC_BYE 4 +# define MB_FUNC_SUSPEND 5 +# define MB_FUNC_END 6 +# define MB_LOOP_BREAK 101 +# define MB_LOOP_CONTINUE 102 +# define MB_SUB_RETURN 103 +# define MB_EXTENDED_ABORT 1001 +#endif /* MB_CODES */ + +#ifndef mb_check +# define mb_check(__r) do { int __hr = __r; if(__hr != MB_FUNC_OK) { return __hr; } } while(0) +#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 */ + +#ifndef mb_convert_to_int_if_posible +# if MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE +# define mb_convert_to_int_if_posible(__v) ((void)(__v)) +# else /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ +# define mb_convert_to_int_if_posible(__v) \ + do { \ + if((__v).type == MB_DT_REAL && (real_t)(int_t)(__v).value.float_point == (__v).value.float_point) { \ + (__v).type = MB_DT_INT; \ + (__v).value.integer = (int_t)(__v).value.float_point; \ + } \ + } while(0) +# endif /* MB_CONVERT_TO_INT_LEVEL == MB_CONVERT_TO_INT_LEVEL_NONE */ +#endif /* mb_convert_to_int_if_posible */ + +struct mb_interpreter_t; + +typedef enum mb_error_e { + SE_NO_ERR = 0, + /** Common */ + SE_CM_OPEN_MB_FAILED, + SE_CM_FUNC_EXISTS, + SE_CM_FUNC_NOT_EXISTS, + SE_CM_NOT_SUPPORTED, + /** Parsing */ + SE_PS_OPEN_FILE_FAILED, + SE_PS_SYMBOL_TOO_LONG, + SE_PS_INVALID_CHAR, + /** Running */ + SE_RN_EMPTY_PROGRAM, + SE_RN_PROGRAM_TOO_LONG, + SE_RN_SYNTAX_ERROR, + SE_RN_INVALID_DATA_TYPE, + SE_RN_TYPE_NOT_MATCH, + SE_RN_NUMBER_OVERFLOW, + SE_RN_INVALID_STRING, + SE_RN_INDEX_OUT_OF_BOUND, + SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, + SE_RN_ILLEGAL_BOUND, + SE_RN_TOO_MANY_DIMENSIONS, + SE_RN_OPERATION_FAILED, + SE_RN_INVALID_OPERATION_USAGE, + SE_RN_DIMENSION_COUNT_OUT_OF_BOUND, + SE_RN_RANK_OUT_OF_BOUND, + SE_RN_NEED_COMPLEX_ARRAY, + SE_RN_LABEL_NOT_EXISTS, + SE_RN_NO_RETURN_POINT, + SE_RN_COLON_EXPECTED, + SE_RN_COMMA_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_NESTED_TOO_DEEP, + SE_RN_INCOMPLETE_STRUCTURE, + SE_RN_FUNCTION_EXPECTED, + SE_RN_VAR_OR_ARRAY_EXPECTED, + SE_RN_ASSIGN_OPERATOR_EXPECTED, + SE_RN_STRING_EXPECTED, + SE_RN_NUMBER_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_DUPLICATE_ID, + 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, + SE_RN_MODULE_NOT_MATCH, + SE_RN_WRONG_FUNCTION_REACHED, + SE_RN_DO_NOT_SUSPEND_IN_A_ROUTINE, + SE_RN_DO_NOT_MIX_INSTRUCTIONAL_AND_STRUCTURED, + SE_RN_INVALID_ROUTINE, + SE_RN_INCOMPLETE_ROUTINE, + SE_RN_ROUTINE_EXPECTED, + SE_RN_DUPLICATE_ROUTINE, + SE_RN_TOO_MANY_ROUTINES, + SE_RN_INVALID_CLASS, + SE_RN_INCOMPLETE_CLASS, + SE_RN_CLASS_EXPECTED, + SE_RN_DUPLICATE_CLASS, + SE_RN_WRONG_META_CLASS, + SE_RN_HASH_AND_COMPARE_MUST_COME_TOGETHER, + SE_RN_CANNOT_CHANGE_ME, + SE_RN_INVALID_LAMBDA, + SE_RN_LIST_EXPECTED, + SE_RN_COLLECTION_EXPECTED, + SE_RN_ITERATOR_EXPECTED, + SE_RN_COLLECTION_OR_ITERATOR_EXPECTED, + SE_RN_COLLECTION_OR_ITERATOR_OR_CLASS_EXPECTED, + SE_RN_INVALID_ITERATOR, + SE_RN_EMPTY_COLLECTION, + SE_RN_REFERENCED_TYPE_EXPECTED, + SE_RN_REFERENCE_COUNT_OVERFLOW, + SE_RN_WEAK_REFERENCE_COUNT_OVERFLOW, + SE_RN_DEBUG_ID_NOT_FOUND, + SE_RN_STACK_TRACE_DISABLED, + /** Extended abort */ + SE_EA_EXTENDED_ABORT, + /** Extra */ + SE_COUNT +} mb_error_e; + +typedef enum mb_data_e { + MB_DT_NIL = 0, + MB_DT_UNKNOWN = 1 << 0, + MB_DT_INT = 1 << 1, + MB_DT_REAL = 1 << 2, + MB_DT_NUM = MB_DT_INT | MB_DT_REAL, + MB_DT_STRING = 1 << 3, + MB_DT_TYPE = 1 << 4, + MB_DT_USERTYPE = 1 << 5, +#ifdef MB_ENABLE_USERTYPE_REF + MB_DT_USERTYPE_REF = 1 << 6, +#endif /* MB_ENABLE_USERTYPE_REF */ + MB_DT_ARRAY = 1 << 7, +#ifdef MB_ENABLE_COLLECTION_LIB + MB_DT_LIST = 1 << 8, + MB_DT_LIST_IT = 1 << 9, + MB_DT_DICT = 1 << 10, + MB_DT_DICT_IT = 1 << 11, + MB_DT_COLLECTION = MB_DT_LIST | MB_DT_DICT, + MB_DT_ITERATOR = MB_DT_LIST_IT | MB_DT_DICT_IT, +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + MB_DT_CLASS = 1 << 12, +#endif /* MB_ENABLE_CLASS */ + MB_DT_ROUTINE = 1 << 13 +} mb_data_e; + +typedef enum mb_meta_func_u { + MB_MF_IS = 1 << 0, + MB_MF_ADD = 1 << 1, + MB_MF_SUB = 1 << 2, + MB_MF_MUL = 1 << 3, + MB_MF_DIV = 1 << 4, + MB_MF_NEG = 1 << 5, + MB_MF_CALC = MB_MF_IS | MB_MF_ADD | MB_MF_SUB | MB_MF_MUL | MB_MF_DIV | MB_MF_NEG, + MB_MF_COLL = 1 << 6, + MB_MF_FUNC = 1 << 7 +} mb_meta_func_u; + +typedef enum mb_meta_status_u { + MB_MS_NONE = 0, + MB_MS_DONE = 1 << 0, + MB_MS_RETURNED = 1 << 1 +} mb_meta_status_u; + +typedef unsigned char mb_val_bytes_t[mb_max(mb_max(sizeof(void*), sizeof(unsigned long)), mb_max(sizeof(int_t), sizeof(real_t)))]; + +typedef union mb_value_u { + int_t integer; + real_t float_point; + char* string; + mb_data_e type; + void* usertype; +#ifdef MB_ENABLE_USERTYPE_REF + void* usertype_ref; +#endif /* MB_ENABLE_USERTYPE_REF */ + void* array; +#ifdef MB_ENABLE_COLLECTION_LIB + void* list; + void* list_it; + void* dict; + void* dict_it; +#endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + void* instance; +#endif /* MB_ENABLE_CLASS */ + void* routine; + mb_val_bytes_t bytes; +} mb_value_u; + +mb_static_assert(sizeof(mb_val_bytes_t) >= sizeof(mb_value_u)); + +typedef struct mb_value_t { + mb_data_e type; + mb_value_u value; +} mb_value_t; + +typedef unsigned short mb_mem_tag_t; + +typedef int (* mb_func_t)(struct mb_interpreter_t*, void**); +typedef int (* mb_has_routine_arg_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, unsigned*, void*); +typedef int (* mb_pop_routine_arg_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, unsigned*, void*, mb_value_t*); +typedef int (* mb_routine_func_t)(struct mb_interpreter_t*, void**, mb_value_t*, unsigned, void*, mb_has_routine_arg_func_t, mb_pop_routine_arg_func_t); +typedef void (* mb_debug_stepped_handler_t)(struct mb_interpreter_t*, void**, char*, int, unsigned short, unsigned short); +typedef void (* mb_error_handler_t)(struct mb_interpreter_t*, enum mb_error_e, char*, char*, int, unsigned short, unsigned short, int); +typedef int (* mb_print_func_t)(const char*, ...); +typedef int (* mb_input_func_t)(char*, int); +typedef int (* mb_import_handler_t)(struct mb_interpreter_t*, const char*); +typedef void (* mb_dtor_func_t)(struct mb_interpreter_t*, void*); +typedef void* (* mb_clone_func_t)(struct mb_interpreter_t*, void*); +typedef unsigned (* mb_hash_func_t)(struct mb_interpreter_t*, void*); +typedef int (* mb_cmp_func_t)(struct mb_interpreter_t*, void*, void*); +typedef int (* mb_fmt_func_t)(struct mb_interpreter_t*, void*, char*, unsigned); +typedef int (* mb_meta_operator_t)(struct mb_interpreter_t*, void**, mb_value_t*, mb_value_t*, mb_value_t*); +typedef mb_meta_status_u (* mb_meta_func_t)(struct mb_interpreter_t*, void**, const char*); +typedef char* (* mb_memory_allocate_func_t)(unsigned); +typedef void (* mb_memory_free_func_t)(char*); + +MBAPI unsigned mb_ver(void); +MBAPI const char* mb_ver_string(void); + +MBAPI int mb_init(void); +MBAPI int mb_dispose(void); +MBAPI int mb_open(struct mb_interpreter_t** s); +MBAPI int mb_close(struct mb_interpreter_t** s); +MBAPI int mb_reset(struct mb_interpreter_t** s, bool_t clrf/* = false*/); + +MBAPI int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f); +MBAPI int mb_remove_func(struct mb_interpreter_t* s, const char* n); +MBAPI int mb_remove_reserved_func(struct mb_interpreter_t* s, const char* n); +MBAPI int mb_begin_module(struct mb_interpreter_t* s, const char* n); +MBAPI int mb_end_module(struct mb_interpreter_t* s); + +MBAPI int mb_attempt_func_begin(struct mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_func_end(struct mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_open_bracket(struct mb_interpreter_t* s, void** l); +MBAPI int mb_attempt_close_bracket(struct mb_interpreter_t* s, void** l); +MBAPI int mb_has_arg(struct mb_interpreter_t* s, void** l); +MBAPI int mb_pop_int(struct mb_interpreter_t* s, void** l, int_t* val); +MBAPI int mb_pop_real(struct mb_interpreter_t* s, void** l, real_t* val); +MBAPI int mb_pop_string(struct mb_interpreter_t* s, void** l, char** val); +MBAPI int mb_pop_usertype(struct mb_interpreter_t* s, void** l, void** val); +MBAPI int mb_pop_value(struct mb_interpreter_t* s, void** l, mb_value_t* val); +MBAPI int mb_push_int(struct mb_interpreter_t* s, void** l, int_t val); +MBAPI int mb_push_real(struct mb_interpreter_t* s, void** l, real_t val); +MBAPI int mb_push_string(struct mb_interpreter_t* s, void** l, char* val); +MBAPI int mb_push_usertype(struct mb_interpreter_t* s, void** l, void* val); +MBAPI int mb_push_value(struct mb_interpreter_t* s, void** l, mb_value_t val); + +MBAPI int mb_begin_class(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t** meta, int c, mb_value_t* out); +MBAPI int mb_end_class(struct mb_interpreter_t* s, void** l); +MBAPI int mb_get_class_userdata(struct mb_interpreter_t* s, void** l, void** d); +MBAPI int mb_set_class_userdata(struct mb_interpreter_t* s, void** l, void* d); + +MBAPI int mb_get_value_by_name(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val); +MBAPI int mb_add_var(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t val, bool_t force); +MBAPI int mb_get_var(struct mb_interpreter_t* s, void** l, void** v); +MBAPI int mb_get_var_value(struct mb_interpreter_t* s, void* v, mb_value_t* val); +MBAPI int mb_set_var_value(struct mb_interpreter_t* s, void* v, mb_value_t val); +MBAPI int mb_init_array(struct mb_interpreter_t* s, void** l, mb_data_e t, int* d, int c, void** a); +MBAPI int mb_get_array_len(struct mb_interpreter_t* s, void** l, void* a, int r, int* i); +MBAPI int mb_get_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t* val); +MBAPI int mb_set_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t val); +MBAPI int mb_init_coll(struct mb_interpreter_t* s, void** l, mb_value_t* coll); +MBAPI int mb_get_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t* val); +MBAPI int mb_set_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t val); +MBAPI int mb_remove_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx); +MBAPI int mb_count_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, int* c); +MBAPI int mb_make_ref_value(struct mb_interpreter_t* s, void* val, mb_value_t* out, mb_dtor_func_t un, mb_clone_func_t cl, mb_hash_func_t hs/* = NULL*/, mb_cmp_func_t cp/* = NULL*/, mb_fmt_func_t ft/* = NULL*/); +MBAPI int mb_get_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val, void** out); +MBAPI int mb_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val); +MBAPI int mb_unref_value(struct mb_interpreter_t* s, void** l, mb_value_t val); +MBAPI int mb_override_value(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_meta_func_u m, void* f); +MBAPI int mb_dispose_value(struct mb_interpreter_t* s, mb_value_t val); + +MBAPI int mb_get_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val); +MBAPI int mb_set_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_routine_func_t f, bool_t force); +MBAPI int mb_eval_routine(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_value_t* args, unsigned argc); + +MBAPI int mb_load_string(struct mb_interpreter_t* s, const char* l, bool_t reset/* = true*/); +MBAPI int mb_load_file(struct mb_interpreter_t* s, const char* f); +MBAPI int mb_run(struct mb_interpreter_t* s); +MBAPI int mb_suspend(struct mb_interpreter_t* s, void** l); +MBAPI int mb_schedule_suspend(struct mb_interpreter_t* s, int t); + +MBAPI int mb_debug_get(struct mb_interpreter_t* s, const char* n, mb_value_t* val); +MBAPI int mb_debug_set(struct mb_interpreter_t* s, const char* n, mb_value_t val); +MBAPI int mb_debug_get_stack_trace(struct mb_interpreter_t* s, void** l, char** fs, unsigned fc); +MBAPI int mb_debug_set_stepped_handler(struct mb_interpreter_t* s, mb_debug_stepped_handler_t h); + +MBAPI const char* mb_get_type_string(mb_data_e t); + +MBAPI int mb_raise_error(struct mb_interpreter_t* s, void** l, mb_error_e err, int ret); +MBAPI mb_error_e mb_get_last_error(struct mb_interpreter_t* s); +MBAPI const char* mb_get_error_desc(mb_error_e err); +MBAPI int mb_set_error_handler(struct mb_interpreter_t* s, mb_error_handler_t h); + +MBAPI int mb_set_printer(struct mb_interpreter_t* s, mb_print_func_t p); +MBAPI int mb_set_inputer(struct mb_interpreter_t* s, mb_input_func_t p); + +MBAPI int mb_gc(struct mb_interpreter_t* s, int_t* collected/* = NULL*/); +MBAPI int mb_get_userdata(struct mb_interpreter_t* s, void** d); +MBAPI int mb_set_userdata(struct mb_interpreter_t* s, void* d); +MBAPI int mb_set_import_handler(struct mb_interpreter_t* s, mb_import_handler_t h); +MBAPI int mb_gets(char* buf, int s); +MBAPI char* mb_memdup(const char* val, unsigned size); +MBAPI int mb_set_memory_manager(mb_memory_allocate_func_t a, mb_memory_free_func_t f); + +#ifdef MB_COMPACT_MODE +# pragma pack() +#endif /* MB_COMPACT_MODE */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MY_BASIC_H__ */ diff --git a/resource/my_basic.rc b/resource/my_basic.rc index 49e9d1e..7198e2e 100755 --- a/resource/my_basic.rc +++ b/resource/my_basic.rc @@ -1,77 +1,77 @@ -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS - -#include "windows.h" - -#undef APSTUDIO_READONLY_SYMBOLS - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) -# ifdef _WIN32 - LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED -# pragma code_page(936) -# endif //_WIN32 - -# ifdef APSTUDIO_INVOKED - - 1 TEXTINCLUDE - BEGIN - "resource.h\0" - END - - 2 TEXTINCLUDE - BEGIN - "#include ""windows.h""\r\n" - "\0" - END - - 3 TEXTINCLUDE - BEGIN - "\r\n" - "\0" - END - -# endif - - IDI_ICON_MAIN ICON "icon.ico" - - VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,0,0 - PRODUCTVERSION 1,2,0,0 - FILEFLAGSMASK 0x17L -# ifdef _DEBUG - FILEFLAGS 0x1L -# else - FILEFLAGS 0x0L -# endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "080404b0" - BEGIN - VALUE "Comments", "For more information, see https://github.com/paladin-t/my_basic/." - VALUE "CompanyName", "Wang Renxin" - VALUE "FileDescription", "MY-BASIC Interpreter for Windows" - VALUE "FileVersion", "1, 2, 0, 0" - VALUE "InternalName", "my_basic" - VALUE "LegalCopyright", "Copyright (C) 2011 - 2016 Wang Renxin" - VALUE "LegalTrademarks", "MY-BASIC" - VALUE "OriginalFilename", "my_basic.exe" - VALUE "ProductName", "MY-BASIC" - VALUE "ProductVersion", "1, 2, 0, 0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x804, 1200 - END - END - -#endif - -#ifndef APSTUDIO_INVOKED - -#endif +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS + +#include "windows.h" + +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +# ifdef _WIN32 + LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +# pragma code_page(936) +# endif //_WIN32 + +# ifdef APSTUDIO_INVOKED + + 1 TEXTINCLUDE + BEGIN + "resource.h\0" + END + + 2 TEXTINCLUDE + BEGIN + "#include ""windows.h""\r\n" + "\0" + END + + 3 TEXTINCLUDE + BEGIN + "\r\n" + "\0" + END + +# endif + + IDI_ICON_MAIN ICON "icon.ico" + + VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,2,0,0 + PRODUCTVERSION 1,2,0,0 + FILEFLAGSMASK 0x17L +# ifdef _DEBUG + FILEFLAGS 0x1L +# else + FILEFLAGS 0x0L +# endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080404b0" + BEGIN + VALUE "Comments", "For more information, see https://github.com/paladin-t/my_basic/." + VALUE "CompanyName", "Wang Renxin" + VALUE "FileDescription", "MY-BASIC Interpreter for Windows" + VALUE "FileVersion", "1, 2, 0, 0" + VALUE "InternalName", "my_basic" + VALUE "LegalCopyright", "Copyright (C) 2011 - 2016 Wang Renxin" + VALUE "LegalTrademarks", "MY-BASIC" + VALUE "OriginalFilename", "my_basic.exe" + VALUE "ProductName", "MY-BASIC" + VALUE "ProductVersion", "1, 2, 0, 0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x804, 1200 + END + END + +#endif + +#ifndef APSTUDIO_INVOKED + +#endif diff --git a/sample/README.md b/sample/README.md index c54278a..5088ff8 100644 --- a/sample/README.md +++ b/sample/README.md @@ -1,40 +1,40 @@ -## Samples of MY-BASIC - -**Copyright (C) 2011 - 2016 Wang Renxin. All rights reserved.** - -This folder contains severial sample code of MY-BASIC. - -### Contents - -**Fundamental** - -Including loop, condition, and other fundamental statements. - - sample01.bas - sample02.bas - sample03.bas - sample04.bas - -**Sub routine** - - sample05.bas - -**Prototype based class** - - sample06.bas - -**A Brainfuck interpreter in MY-BASIC** - - sample07.bas - -**A comprehensive simple game named YARD (Yet Another RPG Dungeon)** - - yard/entity.bas - yard/goal.bas - yard/level.bas - yard/map.bas - yard/monster.bas - yard/npc.bas - yard/player.bas - yard/start.bas - yard/utils.bas +## Samples of MY-BASIC + +**Copyright (C) 2011 - 2016 Wang Renxin. All rights reserved.** + +This folder contains severial sample code of MY-BASIC. + +### Contents + +**Fundamental** + +Including loop, condition, and other fundamental statements. + + sample01.bas + sample02.bas + sample03.bas + sample04.bas + +**Sub routine** + + sample05.bas + +**Prototype based class** + + sample06.bas + +**A Brainfuck interpreter in MY-BASIC** + + sample07.bas + +**A comprehensive simple game named YARD (Yet Another RPG Dungeon)** + + yard/entity.bas + yard/goal.bas + yard/level.bas + yard/map.bas + yard/monster.bas + yard/npc.bas + yard/player.bas + yard/start.bas + yard/utils.bas diff --git a/sample/sample06.bas b/sample/sample06.bas index 15fe2cf..37b61cc 100644 --- a/sample/sample06.bas +++ b/sample/sample06.bas @@ -1,27 +1,27 @@ -' This script is an example of MY-BASIC -' Copyright (c) 2011 - 2016 Wang Renxin. All rights reserved. -' For more information, see https://github.com/paladin-t/my_basic/ - -class animal - def speak(a) - print "Default" + a; - enddef -endclass - -class cat(animal) - def speak(a) - print "Meow" + a; - enddef -endclass - -class dog(animal) - def speak(a) - print "Woof" + a; - enddef -endclass - -c = new(cat) -d = new(dog) - -c.speak("!") -d.speak("!") +' This script is an example of MY-BASIC +' Copyright (c) 2011 - 2016 Wang Renxin. All rights reserved. +' For more information, see https://github.com/paladin-t/my_basic/ + +class animal + def speak(a) + print "Default" + a; + enddef +endclass + +class cat(animal) + def speak(a) + print "Meow" + a; + enddef +endclass + +class dog(animal) + def speak(a) + print "Woof" + a; + enddef +endclass + +c = new(cat) +d = new(dog) + +c.speak("!") +d.speak("!") diff --git a/sample/sample07.bas b/sample/sample07.bas index 7436e9b..9cea725 100644 --- a/sample/sample07.bas +++ b/sample/sample07.bas @@ -1,100 +1,100 @@ -' This script is an example of MY-BASIC -' Copyright (c) 2011 - 2016 Wang Renxin. All rights reserved. -' For more information, see https://github.com/paladin-t/my_basic/ - -def reserve(m, n) - for j = len(m) to n + 2 - push(m, 0) - next -enddef - -def forward(cmd, i) - l = len(cmd) - k = 0 - while i < l - h = mid(cmd, i, 1) - if h = "[" then - k = k + 1 - elseif h = "]" then - k = k - 1 - endif - if h = "]" and k = 0 then - return i - endif - i = i + 1 - wend - - return i -enddef - -def backward(cmd, i) - k = 0 - while i > 0 - h = mid(cmd, i, 1) - if h = "]" then - k = k + 1 - elseif h = "[" then - k = k - 1 - endif - if h = "[" and k = 0 then - return i - endif - i = i - 1 - wend - - return i -enddef - -def brainfuck(cmd) - m = list() - - i = 0 - cursor = 0 - - l = len(cmd) - while i < l - c = mid(cmd, i, 1) - if c = ">" then - cursor = cursor + 1 - elseif c = "<" then - cursor = cursor - 1 - elseif c = "+" then - reserve(m, cursor) - b = m(cursor) - m(cursor) = b + 1 - elseif c = "-" then - reserve(m, cursor) - b = m(cursor) - m(cursor) = b - 1 - elseif c = "." then - reserve(m, cursor) - print chr(m(cursor)) - elseif c = "," then - reserve(m, cursor) - input ch$ - m(cursor) = asc(ch$) - elseif c = "[" then - reserve(m, cursor) - b = m(cursor) - if b = 0 then - i = forward(cmd, i) - endif - elseif c = "]" then - reserve(m, cursor) - b = m(cursor) - if b <> 0 then - i = backward(cmd, i) - endif - endif - i = i + 1 - wend -enddef - -' This is a brainfuck interpreter written with MY-BASIC -input "Input: ", cmd$ -' Use a sample code? -if cmd$ = "hello" then - cmd$ = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." -endif - -brainfuck(cmd$) +' This script is an example of MY-BASIC +' Copyright (c) 2011 - 2016 Wang Renxin. All rights reserved. +' For more information, see https://github.com/paladin-t/my_basic/ + +def reserve(m, n) + for j = len(m) to n + 2 + push(m, 0) + next +enddef + +def forward(cmd, i) + l = len(cmd) + k = 0 + while i < l + h = mid(cmd, i, 1) + if h = "[" then + k = k + 1 + elseif h = "]" then + k = k - 1 + endif + if h = "]" and k = 0 then + return i + endif + i = i + 1 + wend + + return i +enddef + +def backward(cmd, i) + k = 0 + while i > 0 + h = mid(cmd, i, 1) + if h = "]" then + k = k + 1 + elseif h = "[" then + k = k - 1 + endif + if h = "[" and k = 0 then + return i + endif + i = i - 1 + wend + + return i +enddef + +def brainfuck(cmd) + m = list() + + i = 0 + cursor = 0 + + l = len(cmd) + while i < l + c = mid(cmd, i, 1) + if c = ">" then + cursor = cursor + 1 + elseif c = "<" then + cursor = cursor - 1 + elseif c = "+" then + reserve(m, cursor) + b = m(cursor) + m(cursor) = b + 1 + elseif c = "-" then + reserve(m, cursor) + b = m(cursor) + m(cursor) = b - 1 + elseif c = "." then + reserve(m, cursor) + print chr(m(cursor)) + elseif c = "," then + reserve(m, cursor) + input ch$ + m(cursor) = asc(ch$) + elseif c = "[" then + reserve(m, cursor) + b = m(cursor) + if b = 0 then + i = forward(cmd, i) + endif + elseif c = "]" then + reserve(m, cursor) + b = m(cursor) + if b <> 0 then + i = backward(cmd, i) + endif + endif + i = i + 1 + wend +enddef + +' This is a brainfuck interpreter written with MY-BASIC +input "Input: ", cmd$ +' Use a sample code? +if cmd$ = "hello" then + cmd$ = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." +endif + +brainfuck(cmd$) diff --git a/sample/yard/README.md b/sample/yard/README.md index 966ce0f..1bfafa5 100644 --- a/sample/yard/README.md +++ b/sample/yard/README.md @@ -1,25 +1,25 @@ -## YARD (Yet Another RPG Dungeon) - -**Copyright (C) 2016 Wang Renxin. All rights reserved.** - -"Yet Another RPG Dungeon" is a text based game. It's a simple comprehensive example and a tutorial which demonstrates lots of concepts of MY-BASIC. - -### Usage - -**Windows** - - my_basic start.bas - -**OS X** - - my_basic_mac start.bas - -**Others** - - my_basic_bin start.bas - -**Note** - -It requires an importing directories information if the working directory of your interpreter is `my_basic/output/`: - - my_basic -f ../sample/yard/ ../sample/yard/start.bas +## YARD (Yet Another RPG Dungeon) + +**Copyright (C) 2016 Wang Renxin. All rights reserved.** + +"Yet Another RPG Dungeon" is a text based game. It's a simple comprehensive example and a tutorial which demonstrates lots of concepts of MY-BASIC. + +### Usage + +**Windows** + + my_basic start.bas + +**OS X** + + my_basic_mac start.bas + +**Others** + + my_basic_bin start.bas + +**Note** + +It requires an importing directories information if the working directory of your interpreter is `my_basic/output/`: + + my_basic -f ../sample/yard/ ../sample/yard/start.bas diff --git a/sample/yard/entity.bas b/sample/yard/entity.bas index 848fcdf..51292ef 100644 --- a/sample/yard/entity.bas +++ b/sample/yard/entity.bas @@ -1,47 +1,47 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -class entity - var name = "" - - var alive = true - - var hp = 0 - var atk = 0 - var defence = 0 - - var dead_handler = nil - - def tostring() - return "Entity [" + name + "]" - enddef - - def init(_hp, _atk, _def) - hp = _hp - atk = _atk - defence = _def - enddef - - def kill() - if dead_handler <> nil then - dead_handler(me) - endif - alive = false - enddef - - def hurt_by(e) - p = e.atk / defence - if p <= 0 then - p = 1 - endif - hp = hp - p - if hp < 0 then - hp = 0 - endif - if hp = 0 then - kill() - endif - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +class entity + var name = "" + + var alive = true + + var hp = 0 + var atk = 0 + var defence = 0 + + var dead_handler = nil + + def tostring() + return "Entity [" + name + "]" + enddef + + def init(_hp, _atk, _def) + hp = _hp + atk = _atk + defence = _def + enddef + + def kill() + if dead_handler <> nil then + dead_handler(me) + endif + alive = false + enddef + + def hurt_by(e) + p = e.atk / defence + if p <= 0 then + p = 1 + endif + hp = hp - p + if hp < 0 then + hp = 0 + endif + if hp = 0 then + kill() + endif + enddef +endclass diff --git a/sample/yard/goal.bas b/sample/yard/goal.bas index 4d812cf..43cf864 100644 --- a/sample/yard/goal.bas +++ b/sample/yard/goal.bas @@ -1,20 +1,20 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "entity.bas" - -class goal(entity) - var take_handler = nil - - def tostring() - return "Goal [" + name + "]" - enddef - - def take(e) - if take_handler <> nil then - take_handler(me) - endif - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "entity.bas" + +class goal(entity) + var take_handler = nil + + def tostring() + return "Goal [" + name + "]" + enddef + + def take(e) + if take_handler <> nil then + take_handler(me) + endif + enddef +endclass diff --git a/sample/yard/level.bas b/sample/yard/level.bas index c4583ab..903fb6a 100644 --- a/sample/yard/level.bas +++ b/sample/yard/level.bas @@ -1,262 +1,262 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "goal.bas" -import "map.bas" -import "monster.bas" -import "npc.bas" - -class game_status - var invalid = 0 - var playing = 1 - var win = 2 - var lose = 3 -endclass - -class level - var status = game_status.invalid - var roads = new(map) - var role = nil - - def set_node(x, y, ...) - n = new(map_node) - roads.set_node(x, y, n) - n.set_valid_dirs(...) - - return n - enddef - - def get_node(x, y) - return roads.get_node(x, y) - enddef - - def create() - roads.set_size(3, 3) - - n = set_node(0, 0, dirs.right_dir) - - n = set_node(1, 0, dirs.left_dir, dirs.right_dir, dirs.up_dir) - - n = set_node(2, 0, dirs.left_dir, dirs.up_dir) - - m = new(monster) - m.name = "Skeleton" - m.init(7, 1, 1) - m.dead_handler = lambda (_) - ( - print "- " + _.name + " dead"; - ) - n.add_entity(m) - - n = set_node(0, 1, dirs.right_dir) - - n = set_node(1, 1, dirs.left_dir, dirs.up_dir, dirs.down_dir) - - m = new(monster) - m.name = "Slime" - m.init(5, 1, 1) - m.dead_handler = lambda (_) - ( - print "- " + _.name + " dead"; - ) - n.add_entity(m) - - n = set_node(2, 1, dirs.up_dir, dirs.down_dir) - - n = set_node(0, 2, dirs.right_dir) - - g = new(goal) - g.name = "Goal" - n.add_entity(g) - - n = set_node(1, 2, dirs.left_dir, dirs.right_dir, dirs.down_dir) - - m = new(monster) - m.name = "Dragon" - m.init(10, 5, 1) - m.dead_handler = lambda (_) - ( - print "- " + _.name + " dead"; - ) - n.add_entity(m) - - n = set_node(2, 2, dirs.left_dir, dirs.down_dir) - - c = new(npc) - c.name = "Therapist" - c.talk_handler = lambda (_) - ( - print "- " + _.name + ": give you 23 HP"; - _.alive = false - role.hp = role.hp + 23 - ) - n.add_entity(c) - enddef - - def start(r) - status = game_status.playing - role = r - role.pos.x = 0 - role.pos.y = 0 - enddef - - def format() - if status = game_status.win then - print "You Win"; - elseif status = game_status.lose then - print "Game Over"; - - return - elseif status = game_status.playing then - print "<" + str(role.pos.x) + ", " + str(role.pos.y) + ">"; - - n = roads.get_node(role.pos.x, role.pos.y) - - contains_goal = false - gl = n.get_entities(goal) - it = iterator(gl) - if move_next(it) then - contains_goal = true - ent = get(it) - print " ", ent; - it = nil - endif - - contains_npc = false - np = n.get_entities(npc) - it = iterator(np) - if move_next(it) then - contains_npc = true - ent = get(it) - print " ", ent; - it = nil - endif - - contains_monster = false - ms = n.get_entities(monster) - it = iterator(ms) - if move_next(it) then - contains_monster = true - ent = get(it) - print " ", ent; - it = nil - endif - - print "[Operations]"; - - if contains_goal then - print " G: Accomplish goal"; - endif - - if contains_npc then - print " T: Talk to " + ent.name; - endif - - if contains_monster then - print " A: Attack " + ent.name; - else - if bit_and(n.valid_dirs, dirs.left_dir) then - print " W: Go west"; - endif - - if bit_and(n.valid_dirs, dirs.right_dir) then - print " E: Go east"; - endif - - if bit_and(n.valid_dirs, dirs.up_dir) then - print " N: Go north"; - endif - - if bit_and(n.valid_dirs, dirs.down_dir) then - print " S: Go south"; - endif - endif - endif - - print " Q: Quit game"; - enddef - - def update(i) - if i = "" then - return false - endif - - ipt = to_lower_case(i) - - if ipt = "q" then print "Bye."; : end - - if status <> game_status.playing then - return false - endif - - n = roads.get_node(role.pos.x, role.pos.y) - - gl = n.get_entities(goal) - it = iterator(gl) - if ipt = "g" and move_next(it) then - ent = get(it) - it = nil - status = game_status.win - - return true - endif - - np = n.get_entities(npc) - it = iterator(np) - if ipt = "t" and move_next(it) then - ent = get(it) - it = nil - ent.talk(role) - - return true - endif - - contains_monster = false - ms = n.get_entities(monster) - it = iterator(ms) - if move_next(it) then - contains_monster = true - - if ipt = "a" then - ent = get(it) - it = nil - ent.hurt_by(role) - role.hurt_by(ent) - - return true - endif - endif - - if not contains_monster then - if ipt = "w" and bit_and(n.valid_dirs, dirs.left_dir) then - role.pos.x = role.pos.x - 1 - - return true - endif - - if ipt = "e" and bit_and(n.valid_dirs, dirs.right_dir) then - role.pos.x = role.pos.x + 1 - - return true - endif - - if ipt = "n" and bit_and(n.valid_dirs, dirs.up_dir) then - role.pos.y = role.pos.y + 1 - - return true - endif - - if ipt = "s" and bit_and(n.valid_dirs, dirs.down_dir) then - role.pos.y = role.pos.y - 1 - - return true - endif - endif - - print "Invalid input."; - - return false - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "goal.bas" +import "map.bas" +import "monster.bas" +import "npc.bas" + +class game_status + var invalid = 0 + var playing = 1 + var win = 2 + var lose = 3 +endclass + +class level + var status = game_status.invalid + var roads = new(map) + var role = nil + + def set_node(x, y, ...) + n = new(map_node) + roads.set_node(x, y, n) + n.set_valid_dirs(...) + + return n + enddef + + def get_node(x, y) + return roads.get_node(x, y) + enddef + + def create() + roads.set_size(3, 3) + + n = set_node(0, 0, dirs.right_dir) + + n = set_node(1, 0, dirs.left_dir, dirs.right_dir, dirs.up_dir) + + n = set_node(2, 0, dirs.left_dir, dirs.up_dir) + + m = new(monster) + m.name = "Skeleton" + m.init(7, 1, 1) + m.dead_handler = lambda (_) + ( + print "- " + _.name + " dead"; + ) + n.add_entity(m) + + n = set_node(0, 1, dirs.right_dir) + + n = set_node(1, 1, dirs.left_dir, dirs.up_dir, dirs.down_dir) + + m = new(monster) + m.name = "Slime" + m.init(5, 1, 1) + m.dead_handler = lambda (_) + ( + print "- " + _.name + " dead"; + ) + n.add_entity(m) + + n = set_node(2, 1, dirs.up_dir, dirs.down_dir) + + n = set_node(0, 2, dirs.right_dir) + + g = new(goal) + g.name = "Goal" + n.add_entity(g) + + n = set_node(1, 2, dirs.left_dir, dirs.right_dir, dirs.down_dir) + + m = new(monster) + m.name = "Dragon" + m.init(10, 5, 1) + m.dead_handler = lambda (_) + ( + print "- " + _.name + " dead"; + ) + n.add_entity(m) + + n = set_node(2, 2, dirs.left_dir, dirs.down_dir) + + c = new(npc) + c.name = "Therapist" + c.talk_handler = lambda (_) + ( + print "- " + _.name + ": give you 23 HP"; + _.alive = false + role.hp = role.hp + 23 + ) + n.add_entity(c) + enddef + + def start(r) + status = game_status.playing + role = r + role.pos.x = 0 + role.pos.y = 0 + enddef + + def format() + if status = game_status.win then + print "You Win"; + elseif status = game_status.lose then + print "Game Over"; + + return + elseif status = game_status.playing then + print "<" + str(role.pos.x) + ", " + str(role.pos.y) + ">"; + + n = roads.get_node(role.pos.x, role.pos.y) + + contains_goal = false + gl = n.get_entities(goal) + it = iterator(gl) + if move_next(it) then + contains_goal = true + ent = get(it) + print " ", ent; + it = nil + endif + + contains_npc = false + np = n.get_entities(npc) + it = iterator(np) + if move_next(it) then + contains_npc = true + ent = get(it) + print " ", ent; + it = nil + endif + + contains_monster = false + ms = n.get_entities(monster) + it = iterator(ms) + if move_next(it) then + contains_monster = true + ent = get(it) + print " ", ent; + it = nil + endif + + print "[Operations]"; + + if contains_goal then + print " G: Accomplish goal"; + endif + + if contains_npc then + print " T: Talk to " + ent.name; + endif + + if contains_monster then + print " A: Attack " + ent.name; + else + if bit_and(n.valid_dirs, dirs.left_dir) then + print " W: Go west"; + endif + + if bit_and(n.valid_dirs, dirs.right_dir) then + print " E: Go east"; + endif + + if bit_and(n.valid_dirs, dirs.up_dir) then + print " N: Go north"; + endif + + if bit_and(n.valid_dirs, dirs.down_dir) then + print " S: Go south"; + endif + endif + endif + + print " Q: Quit game"; + enddef + + def update(i) + if i = "" then + return false + endif + + ipt = to_lower_case(i) + + if ipt = "q" then print "Bye."; : end + + if status <> game_status.playing then + return false + endif + + n = roads.get_node(role.pos.x, role.pos.y) + + gl = n.get_entities(goal) + it = iterator(gl) + if ipt = "g" and move_next(it) then + ent = get(it) + it = nil + status = game_status.win + + return true + endif + + np = n.get_entities(npc) + it = iterator(np) + if ipt = "t" and move_next(it) then + ent = get(it) + it = nil + ent.talk(role) + + return true + endif + + contains_monster = false + ms = n.get_entities(monster) + it = iterator(ms) + if move_next(it) then + contains_monster = true + + if ipt = "a" then + ent = get(it) + it = nil + ent.hurt_by(role) + role.hurt_by(ent) + + return true + endif + endif + + if not contains_monster then + if ipt = "w" and bit_and(n.valid_dirs, dirs.left_dir) then + role.pos.x = role.pos.x - 1 + + return true + endif + + if ipt = "e" and bit_and(n.valid_dirs, dirs.right_dir) then + role.pos.x = role.pos.x + 1 + + return true + endif + + if ipt = "n" and bit_and(n.valid_dirs, dirs.up_dir) then + role.pos.y = role.pos.y + 1 + + return true + endif + + if ipt = "s" and bit_and(n.valid_dirs, dirs.down_dir) then + role.pos.y = role.pos.y - 1 + + return true + endif + endif + + print "Invalid input."; + + return false + enddef +endclass diff --git a/sample/yard/map.bas b/sample/yard/map.bas index d8ba58d..259570c 100644 --- a/sample/yard/map.bas +++ b/sample/yard/map.bas @@ -1,95 +1,95 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "utils.bas" - -class dirs - var none_dir = 0 - var left_dir = 1 - var right_dir = 2 - var up_dir = 4 - var down_dir = 8 -endclass - -class map_node - var pos = new(point) - var valid_dirs = dirs.none_dir - var entities = list() - - def tostring() - s = pos.tostring() - return "Map Node " + s - enddef - - def set_valid_dirs(...) - dir = 0 - while len(...) - dir = bit_or(dir, ...) - wend - valid_dirs = dir - enddef - - def add_entity(ent) - if exist(entities, ent) then - log.w("Already added " + ent.name) - - return false - endif - - push(entities, ent) - - return true - enddef - - def get_entities(y) - ret = list() - it = iterator(entities) - while move_next(it) - ent = get(it) - if ent is y and ent.alive then - push(ret, ent); - endif - wend - - return ret - enddef -endclass - -class map - var width = 0 - var height = 0 - - var nodes = dict() - - def tostring() - return "Map" - enddef - - def hash_pos(x, y) - return x + y * width - enddef - - def set_size(w, h) - width = w - height = h - - clear(nodes) - enddef - - def set_node(x, y, n) - h = hash_pos(x, y) - n.pos.x = x - n.pos.y = y - nodes(h) = n - - return n - enddef - - def get_node(x, y) - h = hash_pos(x, y) - - return nodes(h) - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "utils.bas" + +class dirs + var none_dir = 0 + var left_dir = 1 + var right_dir = 2 + var up_dir = 4 + var down_dir = 8 +endclass + +class map_node + var pos = new(point) + var valid_dirs = dirs.none_dir + var entities = list() + + def tostring() + s = pos.tostring() + return "Map Node " + s + enddef + + def set_valid_dirs(...) + dir = 0 + while len(...) + dir = bit_or(dir, ...) + wend + valid_dirs = dir + enddef + + def add_entity(ent) + if exist(entities, ent) then + log.w("Already added " + ent.name) + + return false + endif + + push(entities, ent) + + return true + enddef + + def get_entities(y) + ret = list() + it = iterator(entities) + while move_next(it) + ent = get(it) + if ent is y and ent.alive then + push(ret, ent); + endif + wend + + return ret + enddef +endclass + +class map + var width = 0 + var height = 0 + + var nodes = dict() + + def tostring() + return "Map" + enddef + + def hash_pos(x, y) + return x + y * width + enddef + + def set_size(w, h) + width = w + height = h + + clear(nodes) + enddef + + def set_node(x, y, n) + h = hash_pos(x, y) + n.pos.x = x + n.pos.y = y + nodes(h) = n + + return n + enddef + + def get_node(x, y) + h = hash_pos(x, y) + + return nodes(h) + enddef +endclass diff --git a/sample/yard/monster.bas b/sample/yard/monster.bas index 281f781..b03e819 100644 --- a/sample/yard/monster.bas +++ b/sample/yard/monster.bas @@ -1,12 +1,12 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "entity.bas" - -class monster(entity) - def tostring() - return "Monster [" + name + "]" - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "entity.bas" + +class monster(entity) + def tostring() + return "Monster [" + name + "]" + enddef +endclass diff --git a/sample/yard/npc.bas b/sample/yard/npc.bas index caa9d0f..e14b67f 100644 --- a/sample/yard/npc.bas +++ b/sample/yard/npc.bas @@ -1,20 +1,20 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "entity.bas" - -class npc(entity) - var talk_handler = nil - - def tostring() - return "NPC [" + name + "]" - enddef - - def talk(e) - if talk_handler <> nil then - talk_handler(me) - endif - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "entity.bas" + +class npc(entity) + var talk_handler = nil + + def tostring() + return "NPC [" + name + "]" + enddef + + def talk(e) + if talk_handler <> nil then + talk_handler(me) + endif + enddef +endclass diff --git a/sample/yard/player.bas b/sample/yard/player.bas index 42c9d83..8fb3a5b 100644 --- a/sample/yard/player.bas +++ b/sample/yard/player.bas @@ -1,15 +1,15 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "entity.bas" -import "utils.bas" - -class player(entity) - var pos = new(point) - - def tostring() - return "Player [" + name + "]" - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "entity.bas" +import "utils.bas" + +class player(entity) + var pos = new(point) + + def tostring() + return "Player [" + name + "]" + enddef +endclass diff --git a/sample/yard/start.bas b/sample/yard/start.bas index 8986e89..8ad3bea 100644 --- a/sample/yard/start.bas +++ b/sample/yard/start.bas @@ -1,31 +1,31 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -import "level.bas" -import "player.bas" - -cls() -print "Welcome to Yet Another RPG Dungeon!"; - -level.create() -_role = new(player) -_role.init(10, 2, 1) -_role.dead_handler = lambda (_) -( - print "- You are dead"; - level.status = game_status.lose -) -level.start(_role) -_turn = 1 -while _role.alive - print "[---- Turn ", _turn, ", HP ", _role.hp, ", ATK ", _role.atk, " ----]"; - level.format() - input _i$ - cls() - if level.update(_i$) then - _turn = _turn + 1 - endif -wend -level.format() +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +import "level.bas" +import "player.bas" + +cls() +print "Welcome to Yet Another RPG Dungeon!"; + +level.create() +_role = new(player) +_role.init(10, 2, 1) +_role.dead_handler = lambda (_) +( + print "- You are dead"; + level.status = game_status.lose +) +level.start(_role) +_turn = 1 +while _role.alive + print "[---- Turn ", _turn, ", HP ", _role.hp, ", ATK ", _role.atk, " ----]"; + level.format() + input _i$ + cls() + if level.update(_i$) then + _turn = _turn + 1 + endif +wend +level.format() diff --git a/sample/yard/utils.bas b/sample/yard/utils.bas index 06ec903..0c1bceb 100644 --- a/sample/yard/utils.bas +++ b/sample/yard/utils.bas @@ -1,92 +1,92 @@ -' Yet Another RPG Dungeon is a text based game. -' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. -' Copyright (C) 2016 Wang Renxin. All rights reserved. -' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ - -def cls() - if os() = "WIN" then - sys("cls") - else - sys("clear") - endif -enddef - -def to_lower_case(c) - _asc = asc(c) - if _asc >= asc("A") and _asc <= asc("Z") then - _asc = _asc + asc("a") - asc("A") - endif - - return chr(_asc) -enddef - -def bit_and(a, b) - c = 0 - for i = 0 to 31 - if (a mod 2) and (b mod 2) then - t = 1 - else - t = 0 - endif - c = c + t * (2 ^ i) - a = fix(a / 2) - b = fix(b / 2) - next - - return c -enddef - -def bit_or(a, b) - c = 0 - for i = 0 to 31 - if (a mod 2) or (b mod 2) then - t = 1 - else - t = 0 - endif - c = c + t * (2 ^ i) - a = fix(a / 2) - b = fix(b / 2) - next - - return c -enddef - -def bit_xor(a, b) - c = 0 - for i = 0 to 31 - if (a mod 2) <> (b mod 2) then - t = 1 - else - t = 0 - endif - c = c + t * (2 ^ i) - a = fix(a / 2) - b = fix(b / 2) - next - - return c -enddef - -class point - var x = 0 - var y = 0 - - def tostring() - return "[" + str(x) + ", " + str(y) + "]" - enddef -endclass - -class log - def m(msg) - print "Message: " + msg; - enddef - - def w(msg) - print "Warning: " + msg; - enddef - - def e(msg) - print "Error: " + msg; - enddef -endclass +' Yet Another RPG Dungeon is a text based game. +' It's aimed to be a comprehensive example and or a tutorial of MY-BASIC. +' Copyright (C) 2016 Wang Renxin. All rights reserved. +' For more information about MY-BASIC, see https://github.com/paladin-t/my_basic/ + +def cls() + if os() = "WIN" then + sys("cls") + else + sys("clear") + endif +enddef + +def to_lower_case(c) + _asc = asc(c) + if _asc >= asc("A") and _asc <= asc("Z") then + _asc = _asc + asc("a") - asc("A") + endif + + return chr(_asc) +enddef + +def bit_and(a, b) + c = 0 + for i = 0 to 31 + if (a mod 2) and (b mod 2) then + t = 1 + else + t = 0 + endif + c = c + t * (2 ^ i) + a = fix(a / 2) + b = fix(b / 2) + next + + return c +enddef + +def bit_or(a, b) + c = 0 + for i = 0 to 31 + if (a mod 2) or (b mod 2) then + t = 1 + else + t = 0 + endif + c = c + t * (2 ^ i) + a = fix(a / 2) + b = fix(b / 2) + next + + return c +enddef + +def bit_xor(a, b) + c = 0 + for i = 0 to 31 + if (a mod 2) <> (b mod 2) then + t = 1 + else + t = 0 + endif + c = c + t * (2 ^ i) + a = fix(a / 2) + b = fix(b / 2) + next + + return c +enddef + +class point + var x = 0 + var y = 0 + + def tostring() + return "[" + str(x) + ", " + str(y) + "]" + enddef +endclass + +class log + def m(msg) + print "Message: " + msg; + enddef + + def w(msg) + print "Warning: " + msg; + enddef + + def e(msg) + print "Error: " + msg; + enddef +endclass