15082 lines
375 KiB
C
Executable File
15082 lines
375 KiB
C
Executable File
/*
|
|
** 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"
|
|
#ifdef _MSC_VER
|
|
# include <conio.h>
|
|
# include <malloc.h>
|
|
#else /* _MSC_VER */
|
|
# include <stdint.h>
|
|
#endif /* _MSC_VER */
|
|
#include <limits.h>
|
|
#ifndef ARDUINO
|
|
# include <memory.h>
|
|
#endif /* ARDUINO */
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning(push)
|
|
# pragma warning(disable : 4127)
|
|
# pragma warning(disable : 4305)
|
|
# pragma warning(disable : 4309)
|
|
# pragma warning(disable : 4805)
|
|
# pragma warning(disable : 4996)
|
|
#endif /* _MSC_VER */
|
|
|
|
#ifdef __APPLE__
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wunused-function"
|
|
# pragma clang diagnostic ignored "-Wunused-variable"
|
|
#endif /* __APPLE__ */
|
|
|
|
#ifdef __BORLANDC__
|
|
# pragma warn -8004
|
|
# pragma warn -8008
|
|
# pragma warn -8012
|
|
#endif /* __BORLANDC__ */
|
|
|
|
#ifdef MB_COMPACT_MODE
|
|
# pragma pack(1)
|
|
#endif /* MB_COMPACT_MODE */
|
|
|
|
/*
|
|
** {========================================================
|
|
** Data type declarations
|
|
*/
|
|
|
|
/** Macros */
|
|
#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 _WARING_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
|
|
|
|
/* 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 */
|
|
|
|
#define _copy_bytes(__l, __r) do { memcpy((__l), (__r), sizeof(mb_val_bytes_t)); } while(0)
|
|
#define _cmp_bytes(__l, __r) (memcmp((__l), (__r), sizeof(mb_val_bytes_t)))
|
|
|
|
#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 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)
|
|
#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)))
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#define _IS_ROUTINE(__o) ((__o) && ((_object_t*)(__o))->type == _DT_ROUTINE)
|
|
|
|
/* 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
|
|
|
|
#define _META_LIST_MAX_DEPTH UINT_MAX
|
|
|
|
typedef int (* _common_compare)(void*, void*);
|
|
|
|
/* Container operation */
|
|
#define _OP_RESULT_NORMAL 0
|
|
#define _OP_RESULT_DEL_NODE -1
|
|
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 int (* _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 int array_size;
|
|
unsigned int 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",
|
|
/** Parsing */
|
|
"Open file failed",
|
|
"Symbol too long",
|
|
"Invalid character",
|
|
/** Running */
|
|
"Not supported",
|
|
"Empty program",
|
|
"Syntax error",
|
|
"Invalid data type",
|
|
"Type does not match",
|
|
"Invalid string",
|
|
"Index out of bound",
|
|
"Cannot find with given index",
|
|
"Illegal bound",
|
|
"Too much dimensions",
|
|
"Operation failed",
|
|
"Dimension count out of bound",
|
|
"Out of bound",
|
|
"Label does not exist",
|
|
"No return point",
|
|
"Colon expected",
|
|
"Comma or semicolon expected",
|
|
"Array identifier expected",
|
|
"Open bracket expected",
|
|
"Close bracket expected",
|
|
"Array subscript expected",
|
|
"Nested too deep",
|
|
"Structure not completed",
|
|
"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",
|
|
"Wrong function reached",
|
|
"Don't suspend in a routine",
|
|
"Don't mix instructional and structured sub routines",
|
|
"Invalid routine",
|
|
"Routine expected",
|
|
"Duplicate routine",
|
|
"Invalid class",
|
|
"Class expected",
|
|
"Duplicate class",
|
|
"Wrong meta class",
|
|
"Invalid lambda",
|
|
"Collection expected",
|
|
"Iterator expected",
|
|
"Collection or iterator expected",
|
|
"Invalid iterator",
|
|
"Empty collection",
|
|
"Referenced type expected",
|
|
"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_TYPE,
|
|
_DT_INT,
|
|
_DT_REAL,
|
|
_DT_STRING,
|
|
_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;
|
|
|
|
typedef struct _func_t {
|
|
char* name;
|
|
mb_func_t pointer;
|
|
} _func_t;
|
|
|
|
typedef struct _var_t {
|
|
char* name;
|
|
struct _object_t* data;
|
|
#ifdef MB_ENABLE_CLASS
|
|
int pathing;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
} _var_t;
|
|
|
|
struct _ref_t;
|
|
|
|
typedef void (* _unref_func_t)(struct _ref_t*, void*);
|
|
|
|
typedef unsigned int _ref_count_t;
|
|
|
|
typedef struct _ref_t {
|
|
_ref_count_t* count;
|
|
_unref_func_t on_unref;
|
|
_data_e type;
|
|
struct mb_interpreter_t* s;
|
|
} _ref_t;
|
|
|
|
typedef short _lock_t;
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
typedef struct _gc_t {
|
|
_ht_node_t* table;
|
|
_ht_node_t* recursive_table;
|
|
_ht_node_t* collected_table;
|
|
int_t collecting;
|
|
} _gc_t;
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
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;
|
|
} _usertype_ref_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 int count;
|
|
int dimension_count;
|
|
int dimensions[MB_MAX_DIMENSION_COUNT];
|
|
} _array_t;
|
|
|
|
#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 {
|
|
_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;
|
|
|
|
typedef struct _dict_it_t {
|
|
_dict_t* dict;
|
|
bool_t locking;
|
|
unsigned int 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 _CLASS_HASH_FUNC "HASH"
|
|
# define _CLASS_COMPARE_FUNC "COMPARE"
|
|
# define _CLASS_TO_STRING_FUNC "TOSTRING"
|
|
#endif /* MB_ENABLE_CLASS */
|
|
|
|
#ifdef MB_ENABLE_CLASS
|
|
typedef struct _class_t {
|
|
_ref_t ref;
|
|
char* name;
|
|
_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,
|
|
#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_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 { 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 {
|
|
mb_data_e type;
|
|
int_t integer;
|
|
real_t float_point;
|
|
char* string;
|
|
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;
|
|
_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 */
|
|
|
|
#define _MB_MEM_TAG_SIZE (sizeof(mb_mem_tag_t))
|
|
|
|
MBAPI const size_t MB_SIZEOF_4BYTES = 4;
|
|
MBAPI const size_t MB_SIZEOF_8BYTES = 8;
|
|
MBAPI const size_t MB_SIZEOF_32BYTES = 32;
|
|
MBAPI const size_t MB_SIZEOF_64BYTES = 64;
|
|
MBAPI const size_t MB_SIZEOF_128BYTES = 128;
|
|
MBAPI const size_t MB_SIZEOF_256BYTES = 256;
|
|
MBAPI const size_t MB_SIZEOF_512BYTES = 512;
|
|
#ifdef MB_ENABLE_ALLOC_STAT
|
|
MBAPI const size_t MB_SIZEOF_INT = _MB_MEM_TAG_SIZE + sizeof(int);
|
|
MBAPI const size_t MB_SIZEOF_PTR = _MB_MEM_TAG_SIZE + sizeof(intptr_t);
|
|
MBAPI const size_t MB_SIZEOF_LSN = _MB_MEM_TAG_SIZE + sizeof(_ls_node_t);
|
|
MBAPI const size_t MB_SIZEOF_HTN = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t);
|
|
MBAPI const size_t MB_SIZEOF_HTA = _MB_MEM_TAG_SIZE + sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT;
|
|
MBAPI const size_t MB_SIZEOF_OBJ = _MB_MEM_TAG_SIZE + sizeof(_object_t);
|
|
MBAPI const size_t MB_SIZEOF_FUN = _MB_MEM_TAG_SIZE + sizeof(_func_t);
|
|
MBAPI const size_t MB_SIZEOF_VAR = _MB_MEM_TAG_SIZE + sizeof(_var_t);
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
MBAPI const size_t MB_SIZEOF_UTR = _MB_MEM_TAG_SIZE + sizeof(_usertype_ref_t);
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
MBAPI const size_t MB_SIZEOF_ARR = _MB_MEM_TAG_SIZE + sizeof(_array_t);
|
|
MBAPI const size_t MB_SIZEOF_LBL = _MB_MEM_TAG_SIZE + sizeof(_label_t);
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
MBAPI const size_t MB_SIZEOF_LST = _MB_MEM_TAG_SIZE + sizeof(_list_t);
|
|
MBAPI const size_t MB_SIZEOF_DCT = _MB_MEM_TAG_SIZE + sizeof(_dict_t);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
MBAPI const size_t MB_SIZEOF_RTN = _MB_MEM_TAG_SIZE + sizeof(_routine_t);
|
|
#ifdef MB_ENABLE_CLASS
|
|
MBAPI const size_t MB_SIZEOF_CLS = _MB_MEM_TAG_SIZE + sizeof(_class_t);
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#else /* MB_ENABLE_ALLOC_STAT */
|
|
MBAPI const size_t MB_SIZEOF_INT = sizeof(int);
|
|
MBAPI const size_t MB_SIZEOF_PTR = sizeof(intptr_t);
|
|
MBAPI const size_t MB_SIZEOF_LSN = sizeof(_ls_node_t);
|
|
MBAPI const size_t MB_SIZEOF_HTN = sizeof(_ht_node_t);
|
|
MBAPI const size_t MB_SIZEOF_HTA = sizeof(_ht_node_t*) * _HT_ARRAY_SIZE_DEFAULT;
|
|
MBAPI const size_t MB_SIZEOF_OBJ = sizeof(_object_t);
|
|
MBAPI const size_t MB_SIZEOF_FUN = sizeof(_func_t);
|
|
MBAPI const size_t MB_SIZEOF_VAR = sizeof(_var_t);
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
MBAPI const size_t MB_SIZEOF_UTR = sizeof(_usertype_ref_t);
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
MBAPI const size_t MB_SIZEOF_ARR = sizeof(_array_t);
|
|
MBAPI const size_t MB_SIZEOF_LBL = sizeof(_label_t);
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
MBAPI const size_t MB_SIZEOF_LST = sizeof(_list_t);
|
|
MBAPI const size_t MB_SIZEOF_DCT = sizeof(_dict_t);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
MBAPI const size_t MB_SIZEOF_RTN = sizeof(_routine_t);
|
|
#ifdef MB_ENABLE_CLASS
|
|
MBAPI const size_t MB_SIZEOF_CLS = sizeof(_class_t);
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#endif /* MB_ENABLE_ALLOC_STAT */
|
|
|
|
#ifdef MB_ENABLE_SOURCE_TRACE
|
|
static const _object_t _OBJ_INT_UNIT = { _DT_INT, (mb_data_e)1, false, 0, 0, 0 };
|
|
static const _object_t _OBJ_INT_ZERO = { _DT_INT, (mb_data_e)0, false, 0, 0, 0 };
|
|
#else /* MB_ENABLE_SOURCE_TRACE */
|
|
static const _object_t _OBJ_INT_UNIT = { _DT_INT, (mb_data_e)1, false, 0 };
|
|
static const _object_t _OBJ_INT_ZERO = { _DT_INT, (mb_data_e)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
|
|
static const _var_t _VAR_ARGS = { "...", 0, 0 };
|
|
#else /* MB_ENABLE_CLASS */
|
|
static const _var_t _VAR_ARGS = { "...", 0 };
|
|
#endif /* MB_ENABLE_CLASS */
|
|
|
|
/* Parsing context */
|
|
#define _CLASS_STATE_NONE 0
|
|
#define _CLASS_STATE_PROC 1
|
|
|
|
typedef enum _parsing_state_e {
|
|
_PS_NORMAL = 0,
|
|
_PS_STRING,
|
|
_PS_COMMENT
|
|
} _parsing_state_e;
|
|
|
|
typedef enum _symbol_state_e {
|
|
_SS_IDENTIFIER = 0,
|
|
_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;
|
|
_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 {
|
|
bool_t valid;
|
|
_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 */
|
|
char* source_file;
|
|
_ls_node_t* ast;
|
|
_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;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
_routine_t* last_routine;
|
|
_ls_node_t* sub_stack;
|
|
_ls_node_t* suspent_point;
|
|
int schedule_suspend_tag;
|
|
_ls_node_t* temp_values;
|
|
_ls_node_t* lazy_destroy_objects;
|
|
#ifdef MB_ENABLE_GC
|
|
_gc_t gc;
|
|
#endif /* MB_ENABLE_GC */
|
|
int_t no_eat_comma_mark;
|
|
_ls_node_t* skip_to_eoi;
|
|
_ls_node_t* in_neg_expr;
|
|
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;
|
|
#ifdef MB_ENABLE_STACK_TRACE
|
|
_ls_node_t* stack_frames;
|
|
#endif /* MB_ENABLE_STACK_TRACE */
|
|
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;
|
|
void* userdata;
|
|
} 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) ((void)(__o))
|
|
#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)
|
|
#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) \
|
|
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);
|
|
|
|
#define _using_jump_set_of_instructional(__s, __obj, __exit, __result) \
|
|
do { \
|
|
if((__s)->jump_set & (~_JMP_INS)) { \
|
|
_handle_error_on_obj(__s, SE_RN_DONT_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_DONT_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);
|
|
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 int _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op);
|
|
static unsigned int _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op);
|
|
static unsigned int _ls_foreach(_ls_node_t* list, _ls_operation op);
|
|
static void _ls_sort(_ls_node_t* list, _ls_compare cmp);
|
|
static int _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 int _ht_hash_object(void* ht, void* d);
|
|
static unsigned int _ht_hash_string(void* ht, void* d);
|
|
static unsigned int _ht_hash_intptr(void* ht, void* d);
|
|
#ifdef MB_ENABLE_GC
|
|
static unsigned int _ht_hash_ref(void* ht, void* d);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
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);
|
|
#ifdef MB_ENABLE_GC
|
|
static int _ht_cmp_ref(void* d1, void* d2);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
static _ht_node_t* _ht_create(unsigned int size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra);
|
|
static _ls_node_t* _ht_find(_ht_node_t* ht, void* key);
|
|
static unsigned int _ht_set_or_insert(_ht_node_t* ht, void* key, void* value);
|
|
static unsigned int _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp);
|
|
static unsigned int _ht_foreach(_ht_node_t* ht, _ht_operation op);
|
|
static unsigned int _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 int __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 */
|
|
#define _MB_CHECK_MEM_TAG_SIZE(y, s) do { mb_mem_tag_t _tmp = (mb_mem_tag_t)s; if((y)_tmp != s) { mb_assert(0 && "\"mb_mem_tag_t\" is too small."); printf("The type \"mb_mem_tag_t\" is not precise enough to hold the given data, please redefine it!"); } } while(0)
|
|
#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)
|
|
|
|
/** Unicode handling */
|
|
#ifdef MB_ENABLE_UNICODE
|
|
static int mb_uu_ischar(char* ch);
|
|
static int mb_uu_strlen(char* ch);
|
|
static int mb_uu_substr(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 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 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);
|
|
|
|
/** 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 _WARING_AS_ERROR
|
|
# define _handle_error(__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 /* _WARING_AS_ERROR */
|
|
# define _handle_error(__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 /* _WARING_AS_ERROR */
|
|
#ifdef MB_ENABLE_SOURCE_TRACE
|
|
# define _HANDLE_ERROR(__s, __err, __f, __obj, __ret, __exit, __result) _handle_error((__s), (__err), (__f), (__obj)->source_pos, (__obj)->source_row, (__obj)->source_col, (__ret), __exit, (__result))
|
|
#else /* MB_ENABLE_SOURCE_TRACE */
|
|
# define _HANDLE_ERROR(__s, __err, __f, __obj, __ret, __exit, __result) _handle_error((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result))
|
|
#endif /* MB_ENABLE_SOURCE_TRACE */
|
|
#define _handle_error_on_obj(__s, __err, __f, __obj, __ret, __exit, __result) \
|
|
do { \
|
|
if(__obj) { \
|
|
_HANDLE_ERROR((__s), (__err), (__f), (__obj), (__ret), __exit, (__result)); \
|
|
} else { \
|
|
_handle_error((__s), (__err), (__f), 0, 0, 0, (__ret), __exit, (__result)); \
|
|
} \
|
|
} while(0)
|
|
|
|
#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);
|
|
|
|
/** Parsing helpers */
|
|
static char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix);
|
|
|
|
static bool_t _is_blank(char c);
|
|
static bool_t _is_newline(char c);
|
|
static bool_t _is_separator(char c);
|
|
static bool_t _is_bracket(char c);
|
|
static bool_t _is_quotation_mark(char c);
|
|
static bool_t _is_comment(char c);
|
|
static bool_t _is_accessor(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_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);
|
|
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, char c, int pos, unsigned short row, unsigned short col);
|
|
static void _set_error_pos(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col);
|
|
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_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) \
|
|
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; \
|
|
}
|
|
|
|
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);
|
|
|
|
static unsigned _ref(_ref_t* ref, void* data);
|
|
static unsigned _unref(_ref_t* ref, void* data);
|
|
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);
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
static void _gc_add(_ref_t* ref, void* data, _gc_t* gc);
|
|
static void _gc_remove(_ref_t* ref, void* data);
|
|
static int _gc_add_reachable(void* data, void* extra, void* ht);
|
|
static void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht);
|
|
static int _gc_destroy_garbage_in_list(void* data, void* extra, void* gc);
|
|
static int _gc_destroy_garbage_in_dict(void* data, void* extra, void* gc);
|
|
#ifdef MB_ENABLE_CLASS
|
|
static int _gc_destroy_garbage_in_class(void* data, void* extra, void* gc);
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
static int _gc_destroy_garbage_in_lambda(void* data, void* extra, void* 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);
|
|
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);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
#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(const char* n, _data_e t, mb_interpreter_t* s);
|
|
static void _destroy_array(_array_t* arr);
|
|
static void _init_array(_array_t* arr);
|
|
static int _get_array_pos(struct 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 int* index, bool_t* literally);
|
|
static bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned int 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 int index, mb_value_u* val, _data_e* type);
|
|
static void _clear_array(_array_t* arr);
|
|
static bool_t _is_array(void* obj);
|
|
static void _unref_array(_ref_t* ref, void* data);
|
|
|
|
#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);
|
|
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);
|
|
#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** f);
|
|
static bool_t _is_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 _class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg);
|
|
#endif /* MB_ENABLE_CLASS */
|
|
static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f);
|
|
static void _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);
|
|
#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, void* p);
|
|
static int _remove_filled_upvalue(void* data, void* extra, void* u);
|
|
static int _fill_outer_scope(void* data, void* extra, void* t);
|
|
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);
|
|
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);
|
|
#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, 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 _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);
|
|
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 _destroy_object_capsule_only_with_extra(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 _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);
|
|
|
|
static void _stepped(mb_interpreter_t* s, _ls_node_t* ast);
|
|
static int _execute_statement(mb_interpreter_t* s, _ls_node_t** l);
|
|
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** context);
|
|
|
|
#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 _MSC_VER
|
|
# if _MSC_VER < 1300
|
|
# define MB_FUNC 0
|
|
# else /* _MSC_VER < 1300 */
|
|
# define MB_FUNC __FUNCTION__
|
|
# endif /* _MSC_VER < 1300 */
|
|
#elif defined __BORLANDC__
|
|
# define MB_FUNC __FUNC__
|
|
#elif defined __POCC__
|
|
# define MB_FUNC __func__
|
|
#else /* _MSC_VER */
|
|
# define MB_FUNC __FUNCTION__
|
|
#endif /* _MSC_VER */
|
|
|
|
#ifdef _MSC_VER
|
|
# if _MSC_VER < 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, 0, DON(ast), MB_FUNC_ERR, __exit, __result); \
|
|
} while(0)
|
|
# endif /* _MSC_VER < 1300 */
|
|
#endif /* _MSC_VER */
|
|
#ifndef _do_nothing
|
|
# define _do_nothing(__s, __l, __exit, __result) \
|
|
do { \
|
|
_ls_node_t* ast = (_ls_node_t*)(*(__l)); \
|
|
_handle_error_on_obj((__s), SE_RN_WRONG_FUNCTION_REACHED, (char*)MB_FUNC, DON(ast), MB_FUNC_ERR, __exit, __result); \
|
|
} while(0);
|
|
#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_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_set(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_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 */
|
|
static const _func_t _core_libs[] = {
|
|
{ "#", _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 },
|
|
{ "...", _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 */
|
|
|
|
{ "TYPE", _core_type },
|
|
{ "IMPORT", _core_import },
|
|
{ "END", _core_end }
|
|
};
|
|
|
|
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 },
|
|
{ "VAL", _std_val },
|
|
|
|
{ "LEN", _std_len },
|
|
{ "GET", _std_get },
|
|
|
|
{ "PRINT", _std_print },
|
|
{ "INPUT", _std_input }
|
|
};
|
|
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
static const _func_t _coll_libs[] = {
|
|
{ "LIST", _coll_list },
|
|
{ "DICT", _coll_dict },
|
|
{ "PUSH", _coll_push },
|
|
{ "POP", _coll_pop },
|
|
{ "PEEK", _coll_peek },
|
|
{ "INSERT", _coll_insert },
|
|
{ "SORT", _coll_sort },
|
|
{ "EXIST", _coll_exist },
|
|
{ "SET", _coll_set },
|
|
{ "REMOVE", _coll_remove },
|
|
{ "CLEAR", _coll_clear },
|
|
{ "CLONE", _coll_clone },
|
|
{ "ITERATOR", _coll_iterator },
|
|
{ "MOVE_NEXT", _coll_move_next }
|
|
};
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
/* ========================================================} */
|
|
|
|
/*
|
|
** {========================================================
|
|
** Private function definitions
|
|
*/
|
|
|
|
/** List operations */
|
|
int _ls_cmp_data(void* node, void* info) {
|
|
_ls_node_t* n = (_ls_node_t*)node;
|
|
|
|
return (n->data == info) ? 0 : 1;
|
|
}
|
|
|
|
int _ls_cmp_extra(void* node, void* info) {
|
|
_ls_node_t* n = (_ls_node_t*)node;
|
|
|
|
return (n->extra == info) ? 0 : 1;
|
|
}
|
|
|
|
int _ls_cmp_extra_object(void* node, void* info) {
|
|
_ls_node_t* n = (_ls_node_t*)node;
|
|
|
|
return _ht_cmp_object(n->extra, info);
|
|
}
|
|
|
|
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
|
|
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 */
|
|
|
|
_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;
|
|
}
|
|
|
|
_ls_node_t* _ls_create(void) {
|
|
_ls_node_t* result = 0;
|
|
|
|
result = _ls_create_node(0);
|
|
|
|
return result;
|
|
}
|
|
|
|
_ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp) {
|
|
_ls_node_t* result = 0;
|
|
|
|
mb_assert(list && data && cmp);
|
|
|
|
list = list->next;
|
|
while(list) {
|
|
if(!cmp(list->data, data)) {
|
|
result = list;
|
|
|
|
break;
|
|
}
|
|
list = list->next;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
_ls_node_t* _ls_back(_ls_node_t* node) {
|
|
_ls_node_t* result = node;
|
|
|
|
result = result->prev;
|
|
|
|
return result;
|
|
}
|
|
|
|
_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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
_ls_node_t* _ls_front(_ls_node_t* node) {
|
|
_ls_node_t* result = node;
|
|
|
|
result = result->next;
|
|
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
_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;
|
|
}
|
|
|
|
unsigned int _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op) {
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op) {
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ls_foreach(_ls_node_t* list, _ls_operation op) {
|
|
unsigned int 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;
|
|
} else {
|
|
/* Do nothing */
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
void _ls_sort(_ls_node_t* list, _ls_compare cmp) {
|
|
_ls_node_t* ptr = 0;
|
|
void* tmp = 0;
|
|
|
|
mb_assert(list && cmp);
|
|
|
|
list = list->next;
|
|
for( ; list; list = list->next) {
|
|
for(ptr = list; ptr; ptr = ptr->next) {
|
|
if(cmp(list->data, ptr->data) > 0) {
|
|
tmp = ptr->data;
|
|
ptr->data = list->data;
|
|
list->data = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int _ls_count(_ls_node_t* list) {
|
|
union { void* p; unsigned u; } tmp;
|
|
|
|
mb_assert(list);
|
|
|
|
tmp.p = list->data;
|
|
|
|
return tmp.u;
|
|
}
|
|
|
|
bool_t _ls_empty(_ls_node_t* list) {
|
|
bool_t result = false;
|
|
|
|
mb_assert(list);
|
|
|
|
result = 0 == list->next;
|
|
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void _ls_destroy(_ls_node_t* list) {
|
|
_ls_clear(list);
|
|
safe_free(list);
|
|
}
|
|
|
|
int _ls_free_extra(void* data, void* extra) {
|
|
int result = _OP_RESULT_NORMAL;
|
|
mb_unrefvar(data);
|
|
|
|
mb_assert(extra);
|
|
|
|
safe_free(extra);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Dictionary operations */
|
|
unsigned int _ht_hash_object(void* ht, void* d) {
|
|
unsigned int result = 0;
|
|
_ht_node_t* self = (_ht_node_t*)ht;
|
|
_object_t* o = (_object_t*)d;
|
|
size_t i = 0;
|
|
unsigned int 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:
|
|
result = 5 * h + _ht_hash_string(ht, o->data.string);
|
|
|
|
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, 0, 0) == 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) {
|
|
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;
|
|
}
|
|
/* Fall through */
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
default:
|
|
#ifdef MB_ENABLE_CLASS
|
|
_default:
|
|
#endif /* MB_ENABLE_CLASS */
|
|
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;
|
|
}
|
|
|
|
unsigned int _ht_hash_string(void* ht, void* d) {
|
|
unsigned int result = 0;
|
|
_ht_node_t* self = (_ht_node_t*)ht;
|
|
char* s = (char*)d;
|
|
unsigned int h = 0;
|
|
|
|
mb_assert(ht);
|
|
|
|
for( ; *s; ++s)
|
|
h = 5 * h + *s;
|
|
result = h % self->array_size;
|
|
|
|
return result;
|
|
}
|
|
|
|
unsigned int _ht_hash_intptr(void* ht, void* d) {
|
|
unsigned int result = 0;
|
|
_ht_node_t* self = (_ht_node_t*)ht;
|
|
intptr_t i = *(intptr_t*)d;
|
|
|
|
mb_assert(ht);
|
|
|
|
result = (unsigned int)(i % self->array_size);
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
unsigned int _ht_hash_ref(void* ht, void* d) {
|
|
unsigned int result = 0;
|
|
_ht_node_t* self = (_ht_node_t*)ht;
|
|
_ref_t* ref = *(_ref_t**)d;
|
|
|
|
mb_assert(ht);
|
|
|
|
result = (unsigned int)(intptr_t)ref;
|
|
result %= self->array_size;
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
int _ht_cmp_object(void* d1, void* d2) {
|
|
_object_t* o1 = (_object_t*)d1;
|
|
_object_t* o2 = (_object_t*)d2;
|
|
size_t i = 0;
|
|
#ifdef MB_ENABLE_CLASS
|
|
_routine_t* cmp = 0;
|
|
_object_t val;
|
|
#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;
|
|
else if(o2->data.instance->compare) cmp = o2->data.instance->compare;
|
|
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(o2, &va[0]);
|
|
memset(&ast, 0, sizeof(_ls_node_t));
|
|
if(_eval_routine(s, &tmp, va, 1, cmp, 0, 0) == 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)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);
|
|
/* Fall through */
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
default:
|
|
#ifdef MB_ENABLE_CLASS
|
|
_default:
|
|
#endif /* MB_ENABLE_CLASS */
|
|
for(i = 0; i < 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;
|
|
}
|
|
|
|
int _ht_cmp_string(void* d1, void* d2) {
|
|
char* s1 = (char*)d1;
|
|
char* s2 = (char*)d2;
|
|
|
|
return strcmp(s1, s2);
|
|
}
|
|
|
|
int _ht_cmp_intptr(void* d1, void* d2) {
|
|
intptr_t i1 = *(intptr_t*)d1;
|
|
intptr_t i2 = *(intptr_t*)d2;
|
|
int result = 0;
|
|
|
|
if(i1 < i2)
|
|
result = -1;
|
|
else if(i1 > i2)
|
|
result = 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
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;
|
|
int result = 0;
|
|
|
|
if(i < 0)
|
|
result = -1;
|
|
else if(i > 0)
|
|
result = 1;
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
_ht_node_t* _ht_create(unsigned int size, _ht_compare cmp, _ht_hash hs, _ls_operation freeextra) {
|
|
const unsigned int array_size = size ? size : _HT_ARRAY_SIZE_DEFAULT;
|
|
_ht_node_t* result = 0;
|
|
unsigned int 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;
|
|
}
|
|
|
|
_ls_node_t* _ht_find(_ht_node_t* ht, void* key) {
|
|
_ls_node_t* result = 0;
|
|
_ls_node_t* bucket = 0;
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ht_set_or_insert(_ht_node_t* ht, void* key, void* value) {
|
|
unsigned int result = 0;
|
|
_ls_node_t* bucket = 0;
|
|
unsigned int hash_code = 0;
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ht_remove(_ht_node_t* ht, void* key, _ls_compare cmp) {
|
|
unsigned int result = 0;
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ht_foreach(_ht_node_t* ht, _ht_operation op) {
|
|
unsigned int result = 0;
|
|
_ls_node_t* bucket = 0;
|
|
unsigned int 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;
|
|
}
|
|
|
|
unsigned int _ht_count(_ht_node_t* ht) {
|
|
mb_assert(ht);
|
|
|
|
return ht->count;
|
|
}
|
|
|
|
void _ht_clear(_ht_node_t* ht) {
|
|
unsigned int 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;
|
|
}
|
|
}
|
|
|
|
void _ht_destroy(_ht_node_t* ht) {
|
|
unsigned int 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);
|
|
}
|
|
|
|
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 */
|
|
void* mb_malloc(size_t s) {
|
|
/* Allocate a chunk of memory with a specific size */
|
|
char* ret = NULL;
|
|
size_t rs = s;
|
|
#ifdef MB_ENABLE_ALLOC_STAT
|
|
_MB_CHECK_MEM_TAG_SIZE(size_t, s);
|
|
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;
|
|
}
|
|
|
|
void mb_free(void* p) {
|
|
/* Free a chunk of memory */
|
|
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);
|
|
}
|
|
|
|
int mb_memcmp(void* l, void* r, size_t s) {
|
|
/* Compare two chunks of memory */
|
|
char* lc = (char*)l;
|
|
char* rc = (char*)r;
|
|
size_t i = 0;
|
|
|
|
for(i = 0; i < s; i++) {
|
|
if(lc[i] < rc[i]) return -1;
|
|
else if(lc[i] > rc[i]) return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t mb_memtest(void* p, size_t s) {
|
|
/* Detect whether a chunk of memory contains any non-zero byte */
|
|
size_t result = 0;
|
|
size_t i = 0;
|
|
for(i = 0; i < s; i++)
|
|
result += ((unsigned char*)p)[i];
|
|
|
|
return result;
|
|
}
|
|
|
|
char* mb_strdup(const char* p, size_t s) {
|
|
/* Duplicate a string */
|
|
#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 */
|
|
}
|
|
|
|
char* mb_strupr(char* s) {
|
|
/* Change a string to upper case */
|
|
char* t = s;
|
|
|
|
while(*s) {
|
|
*s = toupper(*s);
|
|
++s;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/** Unicode handling */
|
|
#ifdef MB_ENABLE_UNICODE
|
|
int mb_uu_ischar(char* ch) {
|
|
/* Determine whether a buffer is a UTF8 encoded character, and return _TAKEn bytes */
|
|
#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
|
|
}
|
|
|
|
int mb_uu_strlen(char* ch) {
|
|
/* Tell how many UTF8 character are there in a string */
|
|
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;
|
|
}
|
|
|
|
int mb_uu_substr(char* ch, int begin, int count, char** o) {
|
|
/* Retrieve a sub string of a UTF8 string */
|
|
int cnt = 0;
|
|
char* b = 0;
|
|
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] = '\0';
|
|
|
|
return l;
|
|
}
|
|
#endif /* MB_ENABLE_UNICODE */
|
|
|
|
/** Expression processing */
|
|
bool_t _is_operator(mb_func_t op) {
|
|
/* Determine whether a function is an operator */
|
|
bool_t result = false;
|
|
|
|
result =
|
|
(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);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _is_flow(mb_func_t op) {
|
|
/* Determine whether a function is for flow control */
|
|
bool_t result = false;
|
|
|
|
result =
|
|
(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);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _is_unary(mb_func_t op) {
|
|
/* Determine whether a function is unary */
|
|
return op == _core_neg || op == _core_not;
|
|
}
|
|
|
|
char _get_priority(mb_func_t op1, mb_func_t op2) {
|
|
/* Get the priority of two operators */
|
|
char result = '\0';
|
|
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;
|
|
}
|
|
|
|
int _get_priority_index(mb_func_t op) {
|
|
/* Get the index of an operator in the priority table */
|
|
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;
|
|
}
|
|
|
|
_object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status) {
|
|
/* Operate two operands */
|
|
_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;
|
|
}
|
|
|
|
bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj) {
|
|
/* Determine whether an object is an expression termination */
|
|
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;
|
|
}
|
|
|
|
int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val) {
|
|
/* Calculate an expression */
|
|
int result = 0;
|
|
_ls_node_t* ast = 0;
|
|
_running_context_t* running = 0;
|
|
_ls_node_t* garbage = 0;
|
|
_ls_node_t* optr = 0;
|
|
_ls_node_t* opnd = 0;
|
|
_object_t* c = 0;
|
|
_object_t* x = 0;
|
|
_object_t* a = 0;
|
|
_object_t* b = 0;
|
|
_object_t* r = 0;
|
|
_object_t* theta = 0;
|
|
char pri = '\0';
|
|
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))
|
|
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, "#") == 0) ||
|
|
!(((_object_t*)(_ls_back(optr)->data))->type == _DT_FUNC && strcmp(((_object_t*)(_ls_back(optr)->data))->data.func->name, "#") == 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 int arr_idx = 0;
|
|
mb_value_u arr_val;
|
|
_data_e arr_type;
|
|
_object_t* arr_elem = 0;
|
|
|
|
_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, _exit, 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, _exit, 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;
|
|
result = (c->data.func->pointer)(s, (void**)&ast);
|
|
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, _exit, 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 _exit;
|
|
if(f) {
|
|
_handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, 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, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
}
|
|
_ls_pushback(opnd, c);
|
|
f++;
|
|
} else if(c->type == _DT_ROUTINE) {
|
|
_routine:
|
|
ast = ast->prev;
|
|
result = _eval_routine(s, &ast, 0, 0, c->data.routine, _has_routine_lex_arg, _pop_routine_lex_arg);
|
|
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, _exit, result);
|
|
}
|
|
c = _create_object();
|
|
_ls_pushback(garbage, c);
|
|
result = _public_value_to_internal_object(&running->intermediate_value, c);
|
|
if(result != MB_FUNC_OK)
|
|
goto _exit;
|
|
if(f) {
|
|
_handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_ls_pushback(opnd, c);
|
|
f++;
|
|
} else if(c->type == _DT_VAR && c->data.variable->data->type == _DT_ARRAY) {
|
|
unsigned int arr_idx = 0;
|
|
mb_value_u arr_val;
|
|
_data_e arr_type;
|
|
_object_t* arr_elem = 0;
|
|
|
|
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, _exit, 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, _exit, 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
|
|
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;
|
|
}
|
|
}
|
|
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);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, (void**)l));
|
|
|
|
switch(ocoll->type) {
|
|
case _DT_LIST:
|
|
mb_check(mb_pop_int(s, (void**)l, &idx));
|
|
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, _exit, result);
|
|
}
|
|
|
|
break;
|
|
case _DT_DICT:
|
|
mb_make_nil(key);
|
|
mb_check(mb_pop_value(s, (void**)l, &key));
|
|
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, _exit, result);
|
|
}
|
|
|
|
break;
|
|
default: /* Do nothing */
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_attempt_close_bracket(s, (void**)l));
|
|
|
|
c = _create_object();
|
|
_ls_pushback(garbage, c);
|
|
_public_value_to_internal_object(&ret, c);
|
|
|
|
ast = (_ls_node_t*)*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, _exit, result);
|
|
}
|
|
} while(0);
|
|
}
|
|
}
|
|
if(f) {
|
|
_handle_error_on_obj(s, SE_RN_OPERATOR_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, 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, _exit, 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, DON(errn), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_ls_pushback(opnd, r);
|
|
_ls_pushback(garbage, r);
|
|
if(_IS_FUNC(c, _core_close_bracket))
|
|
hack = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(errn) {
|
|
_handle_error_on_obj(s, SE_RN_CLOSE_BRACKET_EXPECTED, s->source_file, DON(errn), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
c = (_object_t*)(_ls_popback(opnd));
|
|
if(!c || !(c->type == _DT_NIL || c->type == _DT_UNKNOWN || c->type == _DT_TYPE ||
|
|
c->type == _DT_INT || c->type == _DT_REAL || c->type == _DT_STRING ||
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
c->type == _DT_LIST || c->type == _DT_LIST_IT || c->type == _DT_DICT || c->type == _DT_DICT_IT ||
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
#ifdef MB_ENABLE_CLASS
|
|
c->type == _DT_CLASS ||
|
|
#endif /* MB_ENABLE_CLASS */
|
|
c->type == _DT_ROUTINE ||
|
|
c->type == _DT_VAR || c->type == _DT_USERTYPE ||
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
c->type == _DT_USERTYPE_REF ||
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
c->type == _DT_ARRAY)
|
|
) {
|
|
_set_current_error(s, SE_RN_INVALID_DATA_TYPE, 0);
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
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, NULL)) {
|
|
_try_clear_intermediate_value(c, 0, s);
|
|
|
|
if(
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
c->type == _DT_USERTYPE_REF ||
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
c->type == _DT_LIST || c->type == _DT_DICT || c->type == _DT_LIST_IT || c->type == _DT_DICT_IT ||
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
#ifdef MB_ENABLE_CLASS
|
|
c->type == _DT_CLASS ||
|
|
#endif /* MB_ENABLE_CLASS */
|
|
c->type == _DT_ARRAY ||
|
|
c->type == _DT_ROUTINE
|
|
) {
|
|
_destroy_object_capsule_only(c, 0);
|
|
} else {
|
|
_destroy_object(c, 0);
|
|
}
|
|
}
|
|
|
|
_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;
|
|
}
|
|
|
|
_ls_node_t* _push_var_args(mb_interpreter_t* s) {
|
|
/* Push current variable arguments list */
|
|
_ls_node_t* result = s->var_args;
|
|
s->var_args = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args) {
|
|
/* Pop current variable arguments list */
|
|
_ls_node_t* var_args = s->var_args;
|
|
s->var_args = last_var_args;
|
|
if(var_args) {
|
|
_ls_foreach(var_args, _destroy_object_capsule_only);
|
|
_ls_destroy(var_args);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
/* Pop an argument from the caller or a variable argument list */
|
|
int result = MB_FUNC_OK;
|
|
_ls_node_t* ast = *l;
|
|
|
|
mb_make_nil(*arg);
|
|
if(ast && _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;
|
|
}
|
|
|
|
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) {
|
|
/* Process arguments of a routine */
|
|
int result = MB_FUNC_OK;
|
|
_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, 0, 0, 0);
|
|
if(rnode)
|
|
var = ((_object_t*)rnode->data)->data.variable;
|
|
|
|
if(proc_ref)
|
|
var->data->ref = true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
if(_IS_VAR_ARGS(var)) {
|
|
if(args) {
|
|
_ls_node_t* ast = *l;
|
|
if(ast) ast = ast->next;
|
|
*l = ast;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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) {
|
|
/* Evaluate a routine */
|
|
int result = MB_FUNC_OK;
|
|
|
|
#ifdef MB_ENABLE_STACK_TRACE
|
|
_ls_pushback(s->stack_frames, r->name);
|
|
#endif /* MB_ENABLE_STACK_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) {
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
_exit:
|
|
#ifdef MB_ENABLE_STACK_TRACE
|
|
_ls_popback(s->stack_frames);
|
|
#endif /* MB_ENABLE_STACK_TRACE */
|
|
|
|
return result;
|
|
}
|
|
|
|
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) {
|
|
/* Evaluate a script routine */
|
|
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);
|
|
|
|
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 = (_ls_node_t*)*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 = (_ls_node_t*)*l;
|
|
_ls_pushback(s->sub_stack, ast);
|
|
|
|
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);
|
|
ast = (_ls_node_t*)*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_DONT_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, true);
|
|
#else /* MB_ENABLE_CLASS */
|
|
_out_of_scope(s, running, 0, 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;
|
|
|
|
mb_make_nil(inte);
|
|
|
|
_swap_public_value(&inte, &running->intermediate_value);
|
|
|
|
_pop_scope(s, true);
|
|
|
|
_assign_public_value(&s->running_context->intermediate_value, &inte);
|
|
|
|
_exit:
|
|
if(result != MB_FUNC_OK)
|
|
_pop_scope(s, true);
|
|
|
|
_error:
|
|
s->last_routine = lastr;
|
|
|
|
_pop_var_args(s, lastv);
|
|
|
|
_tail:
|
|
return result;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
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) {
|
|
/* Evaluate a lambda routine */
|
|
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);
|
|
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 = (_ls_node_t*)*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);
|
|
ast = (_ls_node_t*)*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_DONT_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, 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 */
|
|
|
|
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) {
|
|
/* Evaluate a native routine */
|
|
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, TON(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;
|
|
}
|
|
|
|
int _has_routine_lex_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) {
|
|
/* Detect if there is any more lexical argument */
|
|
mb_unrefvar(va);
|
|
mb_unrefvar(ca);
|
|
mb_unrefvar(ia);
|
|
mb_unrefvar(r);
|
|
|
|
return mb_has_arg(s, l);
|
|
}
|
|
|
|
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) {
|
|
/* Pop a lexical argument */
|
|
mb_unrefvar(va);
|
|
mb_unrefvar(ca);
|
|
mb_unrefvar(ia);
|
|
mb_unrefvar(r);
|
|
|
|
return mb_pop_value(s, l, val);
|
|
}
|
|
|
|
int _has_routine_fun_arg(mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, unsigned* ia, void* r) {
|
|
/* Detect if there is any more argument in the argument list */
|
|
mb_unrefvar(s);
|
|
mb_unrefvar(l);
|
|
mb_unrefvar(va);
|
|
mb_unrefvar(r);
|
|
|
|
return *ia < ca;
|
|
}
|
|
|
|
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) {
|
|
/* Pop an argument from the argument list */
|
|
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;
|
|
}
|
|
|
|
bool_t _is_print_terminal(mb_interpreter_t* s, _object_t* obj) {
|
|
/* Determine whether an object is a PRINT termination */
|
|
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;
|
|
}
|
|
|
|
/** Others */
|
|
void _set_current_error(mb_interpreter_t* s, mb_error_e err, char* f) {
|
|
/* Set current error information */
|
|
mb_assert(s && err >= 0 && err < _countof(_ERR_DESC));
|
|
|
|
if(s->last_error == SE_NO_ERR) {
|
|
s->last_error = err;
|
|
s->last_error_file = f;
|
|
}
|
|
}
|
|
|
|
mb_print_func_t _get_printer(mb_interpreter_t* s) {
|
|
/* Get a print functor of an interpreter */
|
|
mb_assert(s);
|
|
|
|
if(s->printer)
|
|
return s->printer;
|
|
|
|
return printf;
|
|
}
|
|
|
|
mb_input_func_t _get_inputer(mb_interpreter_t* s) {
|
|
/* Get an input functor of an interpreter */
|
|
mb_assert(s);
|
|
|
|
if(s->inputer)
|
|
return s->inputer;
|
|
|
|
return mb_gets;
|
|
}
|
|
|
|
char* _load_file(mb_interpreter_t* s, const char* f, const char* prefix) {
|
|
/* Read all content of a file into a buffer */
|
|
#ifndef 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)) {
|
|
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);
|
|
fclose(fp);
|
|
buf[l] = '\0';
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
#else /* ARDUINO */
|
|
return 0;
|
|
#endif /* ARDUINO */
|
|
}
|
|
|
|
bool_t _is_blank(char c) {
|
|
/* Determine whether a character is blank */
|
|
return
|
|
(' ' == c) || ('\t' == c) ||
|
|
(-17 == c) || (-69 == c) || (-65 == c) ||
|
|
(-2 == c) || (-1 == c);
|
|
}
|
|
|
|
bool_t _is_newline(char c) {
|
|
/* Determine whether a character is newline */
|
|
return ('\r' == c) || ('\n' == c) || (EOF == c);
|
|
}
|
|
|
|
bool_t _is_separator(char c) {
|
|
/* Determine whether a character is separator */
|
|
return (',' == c) || (';' == c) || (':' == c);
|
|
}
|
|
|
|
bool_t _is_bracket(char c) {
|
|
/* Determine whether a character is bracket */
|
|
return ('(' == c) || (')' == c);
|
|
}
|
|
|
|
bool_t _is_quotation_mark(char c) {
|
|
/* Determine whether a character is quotation mark */
|
|
return ('"' == c);
|
|
}
|
|
|
|
bool_t _is_comment(char c) {
|
|
/* Determine whether a character is comment mark */
|
|
return ('\'' == c);
|
|
}
|
|
|
|
bool_t _is_accessor(char c) {
|
|
/* Determine whether a character is accessor char */
|
|
return c == '.';
|
|
}
|
|
|
|
bool_t _is_numeric_char(char c) {
|
|
/* Determine whether a character is numeric char */
|
|
return (c >= '0' && c <= '9') || _is_accessor(c);
|
|
}
|
|
|
|
bool_t _is_identifier_char(char c) {
|
|
/* Determine whether a character is identifier char */
|
|
return
|
|
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
(c == '_') ||
|
|
_is_numeric_char(c) ||
|
|
(c == '$');
|
|
}
|
|
|
|
bool_t _is_operator_char(char c) {
|
|
/* Determine whether a character is operator char */
|
|
return
|
|
(c == '+') || (c == '-') || (c == '*') || (c == '/') ||
|
|
(c == '^') ||
|
|
(c == '(') || (c == ')') ||
|
|
(c == '=') ||
|
|
(c == '>') || (c == '<');
|
|
}
|
|
|
|
bool_t _is_using_char(char c) {
|
|
/* Determine whether a character is module using char */
|
|
return c == '@';
|
|
}
|
|
|
|
bool_t _is_exponent_prefix(char* s, int begin, int end) {
|
|
/* Determine whether current symbol is exponent prefix */
|
|
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;
|
|
}
|
|
|
|
int _append_char_to_symbol(mb_interpreter_t* s, char c) {
|
|
/* Parse a character and append it to current parsing symbol */
|
|
int result = MB_FUNC_OK;
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
context = s->parsing_context;
|
|
|
|
if(_is_accessor(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;
|
|
} else {
|
|
context->current_symbol[context->current_symbol_nonius] = c;
|
|
++context->current_symbol_nonius;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int _cut_symbol(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) {
|
|
/* Cut current symbol when current one parsing is finished */
|
|
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] != '\0') {
|
|
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;
|
|
}
|
|
|
|
int _append_symbol(mb_interpreter_t* s, char* sym, bool_t* delsym, int pos, unsigned short row, unsigned short col) {
|
|
/* Append cut symbol to the AST list */
|
|
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;
|
|
}
|
|
|
|
int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** obj, _ls_node_t*** asgn, bool_t* delsym) {
|
|
/* Create a syntax symbol */
|
|
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 int 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] = '\0';
|
|
*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, 0, 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(sym, _DT_UNKNOWN, s);
|
|
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, 0, 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, 0, 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, 0, 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 {
|
|
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] == '$') ? _DT_STRING : _DT_INT;
|
|
tmp.var->data->data.integer = 0;
|
|
#ifdef MB_ENABLE_CLASS
|
|
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;
|
|
}
|
|
|
|
_data_e _get_symbol_type(mb_interpreter_t* s, char* sym, _raw_t* value) {
|
|
/* Get the type of a syntax symbol */
|
|
_data_e result = _DT_NIL;
|
|
union { real_t float_point; int_t integer; _object_t* obj; _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 == '\0') {
|
|
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 == '\0') {
|
|
memcpy(*value, tmp.any, sizeof(_raw_t));
|
|
|
|
result = _DT_REAL;
|
|
|
|
goto _exit;
|
|
}
|
|
/* String */
|
|
if(sym[0] == '"' && 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] = '\0';
|
|
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)) {
|
|
safe_free(ns);
|
|
} else {
|
|
_ls_pushback(s->using_modules, ns);
|
|
}
|
|
|
|
goto _end_import;
|
|
#else /* MB_ENABLE_MODULE */
|
|
_handle_error_now(s, SE_RN_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);
|
|
safe_free(buf);
|
|
_post_import(s, lf, &pos, &row, &col);
|
|
}
|
|
} else {
|
|
if(!s->import_handler || s->import_handler(s, sym + 1) != MB_FUNC_OK) {
|
|
_handle_error_now(s, SE_PS_FILE_OPEN_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, "NIL")) {
|
|
tmp.integer = ~0;
|
|
memcpy(*value, tmp.any, sizeof(_raw_t));
|
|
|
|
result = _DT_NIL;
|
|
|
|
goto _exit;
|
|
}
|
|
/* _array_t */
|
|
glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, 0, 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] == '$' ? _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, 0, 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);
|
|
if(!_is_identifier_char(sym[0])) {
|
|
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(sym[0])) {
|
|
glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, 0, 0, 0);
|
|
if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_ROUTINE) {
|
|
if(_IS_FUNC(context->last_symbol, _core_def))
|
|
_begin_routine(s);
|
|
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))
|
|
_begin_routine(s);
|
|
if(!_is_identifier_char(sym[0])) {
|
|
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(sym[0])) {
|
|
result = _DT_SEP;
|
|
|
|
goto _exit;
|
|
}
|
|
/* _var_t */
|
|
glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym, 0, 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, 0, 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;
|
|
}
|
|
|
|
int _parse_char(mb_interpreter_t* s, char c, int pos, unsigned short row, unsigned short col) {
|
|
/* Parse a character */
|
|
int result = MB_FUNC_OK;
|
|
_parsing_context_t* context = 0;
|
|
char last_char = '\0';
|
|
|
|
mb_assert(s && s->parsing_context);
|
|
|
|
context = s->parsing_context;
|
|
|
|
last_char = context->current_char;
|
|
context->current_char = c;
|
|
|
|
if(context->parsing_state == _PS_NORMAL) {
|
|
if(c >= 'a' && c <= 'z')
|
|
c += 'A' - 'a';
|
|
|
|
if(_is_blank(c)) { /* \t ' ' */
|
|
_mb_check(result = _cut_symbol(s, pos, row, col), _exit);
|
|
} else if(_is_newline(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(c) || _is_bracket(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_mark(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(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;
|
|
} 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) && (last_char == 'e' || last_char == 'E') && 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(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(s, SE_PS_INVALID_CHAR, s->source_file, pos, row, col, MB_FUNC_ERR, _exit, result);
|
|
}
|
|
} else {
|
|
mb_assert(0 && "Impossible.");
|
|
}
|
|
}
|
|
} else if(context->parsing_state == _PS_STRING) {
|
|
if(_is_quotation_mark(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);
|
|
}
|
|
} else if(context->parsing_state == _PS_COMMENT) {
|
|
if(_is_newline(c)) { /* \r \n EOF */
|
|
context->parsing_state = _PS_NORMAL;
|
|
} else {
|
|
/* Do nothing */
|
|
}
|
|
} else {
|
|
mb_assert(0 && "Unknown parsing state.");
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
void _set_error_pos(mb_interpreter_t* s, int pos, unsigned short row, unsigned short col) {
|
|
/* Set the position of an error */
|
|
mb_assert(s);
|
|
|
|
s->last_error_pos = pos;
|
|
s->last_error_row = row;
|
|
s->last_error_col = col;
|
|
}
|
|
|
|
char* _prev_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) {
|
|
/* Do something before importing another file */
|
|
#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 */
|
|
}
|
|
|
|
char* _post_import(mb_interpreter_t* s, char* lf, int* pos, unsigned short* row, unsigned short* col) {
|
|
/* Do something after importing another file */
|
|
#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 */
|
|
}
|
|
|
|
int_t _get_size_of(_data_e type) {
|
|
/* Get the size of a data 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;
|
|
}
|
|
|
|
bool_t _try_get_value(_object_t* obj, mb_value_u* val, _data_e expected) {
|
|
/* Try to get a value (typed as int_t, real_t or char*) */
|
|
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;
|
|
}
|
|
|
|
bool_t _is_number(void* obj) {
|
|
/* Determine if an object is a number */
|
|
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;
|
|
}
|
|
|
|
bool_t _is_string(void* obj) {
|
|
/* Determine if an object is a string value or a string variable */
|
|
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;
|
|
}
|
|
|
|
char* _extract_string(_object_t* obj) {
|
|
/* Extract a string from an object */
|
|
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;
|
|
}
|
|
|
|
bool_t _lock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) {
|
|
/* Lock a referenced object */
|
|
mb_assert(lk);
|
|
|
|
_ref(ref, obj);
|
|
if(*lk >= 0)
|
|
++(*lk);
|
|
else
|
|
--(*lk);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool_t _unlock_ref_object(_lock_t* lk, _ref_t* ref, void* obj) {
|
|
/* Unlock a referenced object */
|
|
bool_t result = true;
|
|
|
|
mb_assert(lk);
|
|
|
|
if(*lk > 0)
|
|
--(*lk);
|
|
else if(*lk < 0)
|
|
++(*lk);
|
|
else
|
|
result = false;
|
|
_unref(ref, obj);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _write_on_ref_object(_lock_t* lk, _ref_t* ref, void* obj) {
|
|
/* Write operation on a referenced object */
|
|
bool_t result = true;
|
|
mb_unrefvar(ref);
|
|
mb_unrefvar(obj);
|
|
|
|
mb_assert(lk);
|
|
|
|
if(*lk > 0)
|
|
*lk = -(*lk);
|
|
else
|
|
result = false;
|
|
|
|
return result;
|
|
}
|
|
|
|
unsigned _ref(_ref_t* ref, void* data) {
|
|
/* Increase the reference of a stub by 1 */
|
|
mb_unrefvar(data);
|
|
|
|
return ++(*(ref->count));
|
|
}
|
|
|
|
unsigned _unref(_ref_t* ref, void* data) {
|
|
/* Decrease the reference of a stub by 1 */
|
|
unsigned result = 0;
|
|
|
|
result = --(*(ref->count));
|
|
#ifdef MB_ENABLE_GC
|
|
_gc_add(ref, data, 0);
|
|
if(ref->count && !(*ref->count))
|
|
_tidy_intermediate_value(ref, data);
|
|
ref->on_unref(ref, data);
|
|
if(!ref->count)
|
|
_gc_remove(ref, data);
|
|
#else /* MB_ENABLE_GC */
|
|
ref->on_unref(ref, data);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
return result;
|
|
}
|
|
|
|
void _create_ref(_ref_t* ref, _unref_func_t dtor, _data_e t, mb_interpreter_t* s) {
|
|
/* Create a reference stub, initialize the reference count with zero */
|
|
if(ref->count)
|
|
return;
|
|
|
|
ref->count = (unsigned*)mb_malloc(sizeof(unsigned));
|
|
*(ref->count) = 0;
|
|
ref->on_unref = dtor;
|
|
ref->type = t;
|
|
ref->s = s;
|
|
}
|
|
|
|
void _destroy_ref(_ref_t* ref) {
|
|
/* Destroy a reference stub */
|
|
if(!ref->count)
|
|
return;
|
|
|
|
safe_free(ref->count);
|
|
ref->on_unref = 0;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
void _gc_add(_ref_t* ref, void* data, _gc_t* gc) {
|
|
/* Add a referenced object to GC table for later garbage detection */
|
|
_ht_node_t* table = 0;
|
|
|
|
mb_assert(ref && data);
|
|
|
|
if(!ref->count)
|
|
return;
|
|
|
|
if(gc && _ht_find(gc->collected_table, ref))
|
|
return;
|
|
|
|
if(!ref->s->gc.table)
|
|
return;
|
|
|
|
if(ref->s->gc.collecting)
|
|
table = ref->s->gc.recursive_table;
|
|
else
|
|
table = ref->s->gc.table;
|
|
|
|
if(ref->count && *ref->count)
|
|
_ht_set_or_insert(table, ref, data);
|
|
else
|
|
_ht_remove(table, ref, 0);
|
|
}
|
|
|
|
void _gc_remove(_ref_t* ref, void* data) {
|
|
/* Remove a referenced object from GC */
|
|
_ht_node_t* table = 0;
|
|
|
|
mb_assert(ref && data);
|
|
|
|
if(ref->s->gc.collecting)
|
|
table = ref->s->gc.recursive_table;
|
|
else
|
|
table = ref->s->gc.table;
|
|
|
|
if(table)
|
|
_ht_remove(table, ref, 0);
|
|
}
|
|
|
|
int _gc_add_reachable(void* data, void* extra, void* ht) {
|
|
/* Get reachable objects */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
_var_t* var = 0;
|
|
_ht_node_t* htable = (_ht_node_t*)ht;
|
|
|
|
mb_assert(data && ht);
|
|
|
|
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, htable);
|
|
|
|
break;
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
case _DT_USERTYPE_REF:
|
|
if(!_ht_find(htable, &obj->data.usertype_ref->ref))
|
|
_ht_set_or_insert(htable, &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(htable, &obj->data.array->ref))
|
|
_ht_set_or_insert(htable, &obj->data.array->ref, obj->data.array);
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_ARRAY_REF */
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
case _DT_LIST:
|
|
if(!_ht_find(htable, &obj->data.list->ref)) {
|
|
_ht_set_or_insert(htable, &obj->data.list->ref, obj->data.list);
|
|
_LS_FOREACH(obj->data.list->list, _do_nothing_on_object, _gc_add_reachable, htable);
|
|
}
|
|
|
|
break;
|
|
case _DT_DICT:
|
|
if(!_ht_find(htable, &obj->data.dict->ref)) {
|
|
_ht_set_or_insert(htable, &obj->data.dict->ref, obj->data.dict);
|
|
_HT_FOREACH(obj->data.dict->dict, _do_nothing_on_object, _gc_add_reachable, htable);
|
|
}
|
|
|
|
break;
|
|
case _DT_LIST_IT:
|
|
if(!_ht_find(htable, &obj->data.list_it->list->ref)) {
|
|
_ht_set_or_insert(htable, &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, htable);
|
|
}
|
|
|
|
break;
|
|
case _DT_DICT_IT:
|
|
if(!_ht_find(htable, &obj->data.dict_it->dict->ref)) {
|
|
_ht_set_or_insert(htable, &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, htable);
|
|
}
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
#ifdef MB_ENABLE_CLASS
|
|
case _DT_CLASS:
|
|
if(!_ht_find(htable, &obj->data.instance->ref)) {
|
|
_ht_set_or_insert(htable, &obj->data.instance->ref, obj->data.instance);
|
|
_traverse_class(obj->data.instance, _gc_add_reachable, _add_class_meta_reachable, _META_LIST_MAX_DEPTH, false, htable, 0);
|
|
}
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
default: /* Do nothing */
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
void _gc_get_reachable(mb_interpreter_t* s, _ht_node_t* ht) {
|
|
/* Get all reachable referenced objects */
|
|
_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;
|
|
}
|
|
}
|
|
|
|
int _gc_destroy_garbage_in_list(void* data, void* extra, void* gc) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind, and add it to GC if possible */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
_gc_t* _gc = (_gc_t*)gc;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
_ADDGC(obj, _gc);
|
|
safe_free(obj);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _gc_destroy_garbage_in_dict(void* data, void* extra, void* gc) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
_gc_t* _gc = (_gc_t*)gc;
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
_ADDGC(obj, _gc);
|
|
safe_free(obj);
|
|
|
|
obj = (_object_t*)extra;
|
|
_ADDGC(obj, _gc);
|
|
safe_free(obj);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_CLASS
|
|
int _gc_destroy_garbage_in_class(void* data, void* extra, void* gc) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
_gc_t* _gc = (_gc_t*)gc;
|
|
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);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_CLASS */
|
|
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
int _gc_destroy_garbage_in_lambda(void* data, void* extra, void* gc) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well, and add it to GC if possible */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
_gc_t* _gc = (_gc_t*)gc;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
if(obj->type == _DT_VAR) {
|
|
_gc_destroy_garbage_in_lambda(obj->data.variable->data, 0, gc);
|
|
safe_free(obj->data.variable->name);
|
|
safe_free(obj->data.variable);
|
|
} else {
|
|
_ADDGC(obj, _gc);
|
|
}
|
|
safe_free(obj);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
void _gc_destroy_garbage_in_outer_scope(_running_context_ref_t* p, _gc_t* gc) {
|
|
/* Collect garbage of outer scopes */
|
|
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 */
|
|
|
|
int _gc_destroy_garbage(void* data, void* extra) {
|
|
/* Destroy a garbage */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_gc_t* gc = 0;
|
|
_ref_t* ref = 0;
|
|
bool_t cld = false;
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
_list_t* lst = 0;
|
|
_dict_t* dct = 0;
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
#ifdef MB_ENABLE_CLASS
|
|
_class_t* instance = 0;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
_routine_t* routine = 0;
|
|
#endif /* MB_ENABLE_LAMBDA */
|
|
|
|
mb_assert(data && extra);
|
|
|
|
ref = (_ref_t*)extra;
|
|
gc = &ref->s->gc;
|
|
switch(ref->type) {
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
case _DT_LIST:
|
|
lst = (_list_t*)data;
|
|
if(gc->collecting <= 1 && !_ht_find(gc->recursive_table, ref)) {
|
|
_LS_FOREACH(lst->list, _do_nothing_on_object, _gc_destroy_garbage_in_list, gc);
|
|
} else {
|
|
_ls_foreach(lst->list, _destroy_object_capsule_only);
|
|
}
|
|
_ls_clear(lst->list);
|
|
lst->count = 0;
|
|
|
|
break;
|
|
case _DT_DICT:
|
|
dct = (_dict_t*)data;
|
|
if(gc->collecting <= 1 && !_ht_find(gc->recursive_table, ref)) {
|
|
_HT_FOREACH(dct->dict, _do_nothing_on_object, _gc_destroy_garbage_in_dict, gc);
|
|
} else {
|
|
_ht_foreach(dct->dict, _destroy_object_capsule_only_with_extra);
|
|
}
|
|
_ht_clear(dct->dict);
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
#ifdef MB_ENABLE_CLASS
|
|
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;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
#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);
|
|
_gc_destroy_garbage_in_outer_scope(routine->func.lambda.outer_scope, gc);
|
|
}
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_LAMBDA */
|
|
default: /* Do nothing */
|
|
break;
|
|
}
|
|
if(ref->count) {
|
|
cld = (*(ref->count)) == 1;
|
|
_unref(ref, data);
|
|
if(cld)
|
|
_ht_set_or_insert(gc->collected_table, ref, data);
|
|
}
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
void _gc_swap_tables(mb_interpreter_t* s) {
|
|
/* Swap active garbage table and recursive table */
|
|
_ht_node_t* tmp = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
tmp = s->gc.table;
|
|
s->gc.table = s->gc.recursive_table;
|
|
s->gc.recursive_table = tmp;
|
|
}
|
|
|
|
void _gc_try_trigger(mb_interpreter_t* s) {
|
|
/* Try trigger garbage collection */
|
|
mb_assert(s);
|
|
|
|
if(_ht_count(s->gc.table) >= MB_GC_GARBAGE_THRESHOLD)
|
|
_gc_collect_garbage(s, 1);
|
|
}
|
|
|
|
void _gc_collect_garbage(mb_interpreter_t* s, int depth) {
|
|
/* Collect all garbage */
|
|
_ht_node_t* valid = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
/* Avoid infinity loop */
|
|
if(s->gc.collecting) return;
|
|
s->gc.collecting++;
|
|
/* Get reachable information */
|
|
valid = _ht_create(0, _ht_cmp_ref, _ht_hash_ref, _do_nothing_on_object);
|
|
_gc_get_reachable(s, valid);
|
|
/* Get unreachable information */
|
|
_HT_FOREACH(valid, _do_nothing_on_object, _ht_remove_exist, s->gc.table);
|
|
/* Collect garbage */
|
|
do {
|
|
_ht_foreach(s->gc.table, _gc_destroy_garbage);
|
|
_ht_clear(s->gc.table);
|
|
if(s->gc.collecting > 1)
|
|
s->gc.collecting--;
|
|
|
|
if(!depth || !_ht_count(s->gc.recursive_table))
|
|
break;
|
|
|
|
_gc_swap_tables(s);
|
|
s->gc.collecting++;
|
|
} while(1);
|
|
/* Tidy */
|
|
_ht_clear(s->gc.collected_table);
|
|
_ht_clear(valid);
|
|
_ht_destroy(valid);
|
|
s->gc.collecting--;
|
|
mb_assert(!s->gc.collecting);
|
|
}
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
#ifdef MB_ENABLE_USERTYPE_REF
|
|
_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) {
|
|
/* Create a referenced usertype */
|
|
_usertype_ref_t* 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;
|
|
}
|
|
|
|
void _destroy_usertype_ref(_usertype_ref_t* c) {
|
|
/* Destroy a referenced usertype */
|
|
if(c->dtor)
|
|
c->dtor(c->ref.s, c->usertype);
|
|
_destroy_ref(&c->ref);
|
|
safe_free(c);
|
|
}
|
|
|
|
void _unref_usertype_ref(_ref_t* ref, void* data) {
|
|
/* Unreference a referenced usertype */
|
|
if(!(*(ref->count)))
|
|
_destroy_usertype_ref((_usertype_ref_t*)data);
|
|
}
|
|
#endif /* MB_ENABLE_USERTYPE_REF */
|
|
|
|
_array_t* _create_array(const char* n, _data_e t, mb_interpreter_t* s) {
|
|
/* Create an array */
|
|
_array_t* 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;
|
|
}
|
|
|
|
void _destroy_array(_array_t* arr) {
|
|
/* Destroy an array */
|
|
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);
|
|
}
|
|
|
|
void _init_array(_array_t* arr) {
|
|
/* Initialize an array */
|
|
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 int ul = 0;
|
|
for(ul = 0; ul < arr->count; ++ul) {
|
|
arr->types[ul] = _DT_INT;
|
|
}
|
|
}
|
|
#endif /* MB_SIMPLE_ARRAY */
|
|
}
|
|
|
|
int _get_array_pos(struct mb_interpreter_t* s, _array_t* arr, int* d, int c) {
|
|
/* Calculate the true index of an array */
|
|
int result = 0;
|
|
int i = 0;
|
|
int n = 0;
|
|
|
|
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 < 0 || n >= arr->dimensions[i]) {
|
|
result = -1;
|
|
|
|
goto _exit;
|
|
}
|
|
if(result)
|
|
result *= n;
|
|
else
|
|
result += n;
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _get_array_index(mb_interpreter_t* s, _ls_node_t** l, _object_t* c, unsigned int* index, bool_t* literally) {
|
|
/* Calculate the true index of an array, used when walking through an AST */
|
|
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;
|
|
unsigned int idx = 0;
|
|
|
|
mb_assert(s && l && index);
|
|
|
|
subscript_ptr = &subscript;
|
|
_MAKE_NIL(subscript_ptr);
|
|
|
|
if(literally) *literally = false;
|
|
|
|
/* Array name */
|
|
ast = (_ls_node_t*)*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_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
if((int)val.integer >= arr->data.array->dimensions[dcount]) {
|
|
_handle_error_on_obj(s, SE_RN_ARRAY_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
if(idx)
|
|
idx *= (unsigned int)val.integer;
|
|
else
|
|
idx += (unsigned int)val.integer;
|
|
/* Comma? */
|
|
if(_IS_SEP(ast->data, ','))
|
|
ast = ast->next;
|
|
|
|
++dcount;
|
|
}
|
|
*index = idx;
|
|
if(!arr->data.array->raw) {
|
|
_handle_error_on_obj(s, SE_RN_ARRAY_OUT_OF_BOUND, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
_exit:
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _get_array_elem(mb_interpreter_t* s, _array_t* arr, unsigned int index, mb_value_u* val, _data_e* type) {
|
|
/* Get the value of an element in an array */
|
|
bool_t result = true;
|
|
int_t elemsize = 0;
|
|
unsigned int pos = 0;
|
|
void* rawptr = 0;
|
|
|
|
mb_assert(s && arr && val && type);
|
|
|
|
mb_assert(index < arr->count);
|
|
|
|
elemsize = _get_size_of(arr->type);
|
|
pos = (unsigned int)(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;
|
|
}
|
|
|
|
int _set_array_elem(mb_interpreter_t* s, _ls_node_t* ast, _array_t* arr, unsigned int index, mb_value_u* val, _data_e* type) {
|
|
/* Set the value of an element in an array */
|
|
int result = MB_FUNC_OK;
|
|
int_t elemsize = 0;
|
|
unsigned int 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 int)(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;
|
|
}
|
|
|
|
void _clear_array(_array_t* arr) {
|
|
/* Clear an array */
|
|
char* str = 0;
|
|
int_t elemsize = 0;
|
|
unsigned int pos = 0;
|
|
void* rawptr = 0;
|
|
unsigned int 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 int)(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 int)(elemsize * ul);
|
|
rawptr = (void*)((intptr_t)arr->raw + pos);
|
|
str = *((char**)rawptr);
|
|
if(str) {
|
|
safe_free(str);
|
|
}
|
|
}
|
|
}
|
|
safe_free(arr->raw);
|
|
}
|
|
}
|
|
|
|
bool_t _is_array(void* obj) {
|
|
/* Determine if an object is an array value or an array variable */
|
|
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;
|
|
}
|
|
|
|
void _unref_array(_ref_t* ref, void* data) {
|
|
/* Unreference an array */
|
|
if(!(*(ref->count)))
|
|
_destroy_array((_array_t*)data);
|
|
}
|
|
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
_list_t* _create_list(mb_interpreter_t* s) {
|
|
/* Create a list */
|
|
_list_t* 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;
|
|
}
|
|
|
|
void _destroy_list(_list_t* c) {
|
|
/* Destroy a list */
|
|
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);
|
|
}
|
|
|
|
_dict_t* _create_dict(mb_interpreter_t* s) {
|
|
/* Create a dictionary */
|
|
_dict_t* 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;
|
|
}
|
|
|
|
void _destroy_dict(_dict_t* c) {
|
|
/* Destroy a dictionary */
|
|
_ht_foreach(c->dict, _destroy_object_with_extra);
|
|
_ht_destroy(c->dict);
|
|
_destroy_ref(&c->ref);
|
|
safe_free(c);
|
|
}
|
|
|
|
_list_it_t* _create_list_it(_list_t* coll, bool_t lock) {
|
|
/* Create an iterator of a list */
|
|
_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);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _destroy_list_it(_list_it_t* it) {
|
|
/* Destroy an iterator of a list */
|
|
bool_t result = true;
|
|
|
|
mb_assert(it);
|
|
|
|
_unlock_ref_object(&it->list->lock, &it->list->ref, it->list);
|
|
safe_free(it);
|
|
|
|
return result;
|
|
}
|
|
|
|
_list_it_t* _move_list_it_next(_list_it_t* it) {
|
|
/* Move an iterator of a list to next step */
|
|
_list_it_t* result = 0;
|
|
|
|
if(!it || !it->list || !it->list->list || (!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;
|
|
}
|
|
|
|
_dict_it_t* _create_dict_it(_dict_t* coll, bool_t lock) {
|
|
/* Create an iterator of a dictionary */
|
|
_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 = (_ls_node_t*)(intptr_t)~0;
|
|
if(lock)
|
|
_lock_ref_object(&coll->lock, &coll->ref, coll);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _destroy_dict_it(_dict_it_t* it) {
|
|
/* Destroy an iterator of a dictionary */
|
|
bool_t result = true;
|
|
|
|
mb_assert(it);
|
|
|
|
_unlock_ref_object(&it->dict->lock, &it->dict->ref, it->dict);
|
|
safe_free(it);
|
|
|
|
return result;
|
|
}
|
|
|
|
_dict_it_t* _move_dict_it_next(_dict_it_t* it) {
|
|
/* Move an iterator of a dictionary to next step */
|
|
_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 != (_ls_node_t*)(intptr_t)~0) {
|
|
it->curr_node = it->curr_node->next;
|
|
if(!it->curr_node)
|
|
++it->curr_bucket;
|
|
}
|
|
if(!it->curr_node || it->curr_node == (_ls_node_t*)(intptr_t)~0) {
|
|
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;
|
|
}
|
|
|
|
void _unref_list(_ref_t* ref, void* data) {
|
|
/* Unreference a list */
|
|
if(!(*(ref->count)))
|
|
_destroy_list((_list_t*)data);
|
|
}
|
|
|
|
void _unref_dict(_ref_t* ref, void* data) {
|
|
/* Unreference a dictionary */
|
|
if(!(*(ref->count)))
|
|
_destroy_dict((_dict_t*)data);
|
|
}
|
|
|
|
void _push_list(_list_t* coll, mb_value_t* val, _object_t* oarg) {
|
|
/* Push a value to a list */
|
|
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);
|
|
}
|
|
|
|
bool_t _pop_list(_list_t* coll, mb_value_t* val, mb_interpreter_t* s) {
|
|
/* Pop a value from a list */
|
|
_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;
|
|
}
|
|
}
|
|
|
|
bool_t _insert_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) {
|
|
/* Insert a value into a list */
|
|
_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;
|
|
}
|
|
|
|
bool_t _set_list(_list_t* coll, int_t idx, mb_value_t* val, _object_t** oval) {
|
|
/* Set an element in a list with a specific index with a given value */
|
|
_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);
|
|
}
|
|
|
|
bool_t _remove_at_list(_list_t* coll, int_t idx) {
|
|
/* Remove an element in a list with a specific index */
|
|
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;
|
|
}
|
|
|
|
_ls_node_t* _node_at_list(_list_t* coll, int index) {
|
|
/* Get a node in a list with a specific index */
|
|
_ls_node_t* result = 0;
|
|
_ls_node_t* tmp = 0;
|
|
int idx = index;
|
|
|
|
mb_assert(coll);
|
|
|
|
_fill_ranged(coll);
|
|
|
|
/* TODO: Optimize me */
|
|
if(index >= 0 && index < (int)coll->count) {
|
|
if(coll->cached_node && !(index < coll->cached_index / 2)) {
|
|
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 {
|
|
tmp = coll->list->next;
|
|
while(tmp && idx) {
|
|
tmp = tmp->next;
|
|
--idx;
|
|
}
|
|
if(tmp) {
|
|
result = tmp;
|
|
coll->cached_node = tmp;
|
|
coll->cached_index = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool_t _at_list(_list_t* coll, int_t idx, mb_value_t* oval) {
|
|
/* Get the value in a list with a specific index */
|
|
_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);
|
|
}
|
|
|
|
bool_t _find_list(_list_t* coll, mb_value_t* val) {
|
|
/* Find a value in a list */
|
|
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);
|
|
_destroy_object(oarg, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
void _clear_list(_list_t* coll) {
|
|
/* Clear a list */
|
|
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);
|
|
}
|
|
|
|
void _sort_list(_list_t* coll) {
|
|
/* Sort a list */
|
|
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);
|
|
}
|
|
|
|
void _invalidate_list_cache(_list_t* coll) {
|
|
/* Invalidate cached list index */
|
|
mb_assert(coll);
|
|
|
|
coll->cached_node = 0;
|
|
coll->cached_index = 0;
|
|
}
|
|
|
|
void _fill_ranged(_list_t* coll) {
|
|
/* Fill a ranged list with numbers */
|
|
_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);
|
|
}
|
|
}
|
|
|
|
void _set_dict(_dict_t* coll, mb_value_t* key, mb_value_t* val, _object_t* okey, _object_t* oval) {
|
|
/* Set an element to a dictionary with a key-value pair */
|
|
_ls_node_t* exist = 0;
|
|
void* data = 0;
|
|
void* extra = 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) {
|
|
data = exist->data;
|
|
extra = exist->extra;
|
|
_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);
|
|
}
|
|
|
|
bool_t _remove_dict(_dict_t* coll, mb_value_t* key) {
|
|
/* Remove an element to a dictionary with a specific 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);
|
|
}
|
|
|
|
bool_t _find_dict(_dict_t* coll, mb_value_t* val, mb_value_t* oval) {
|
|
/* Find a key in a dictionary */
|
|
_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 && result && result->data)
|
|
_internal_object_to_public_value((_object_t*)result->data, oval);
|
|
|
|
return !!(result && result->data);
|
|
}
|
|
|
|
void _clear_dict(_dict_t* coll) {
|
|
/* Clear a dictionary */
|
|
mb_assert(coll);
|
|
|
|
_ht_foreach(coll->dict, _destroy_object_with_extra);
|
|
_ht_clear(coll->dict);
|
|
|
|
_write_on_ref_object(&coll->lock, &coll->ref, coll);
|
|
}
|
|
|
|
bool_t _invalid_list_it(_list_it_t* it) {
|
|
/* Determin whether a list iterator is invalid */
|
|
if(!it) return false;
|
|
|
|
return it && it->list && it->list->lock <= 0;
|
|
}
|
|
|
|
bool_t _invalid_dict_it(_dict_it_t* it) {
|
|
/* Determin whether a dictionary iterator is invalid */
|
|
if(!it) return false;
|
|
|
|
return it && it->dict && it->dict->lock <= 0;
|
|
}
|
|
|
|
bool_t _assign_with_it(_object_t* tgt, _object_t* src) {
|
|
/* Assign an iterator to another object */
|
|
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;
|
|
}
|
|
|
|
int _clone_to_list(void* data, void* extra, _list_t* coll) {
|
|
/* Clone an object to a list */
|
|
_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);
|
|
_push_list(coll, 0, tgt);
|
|
_REF(tgt)
|
|
|
|
return 1;
|
|
}
|
|
|
|
int _clone_to_dict(void* data, void* extra, _dict_t* coll) {
|
|
/* Clone a key-value pair to a dictionary */
|
|
_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);
|
|
|
|
vtgt = _create_object();
|
|
vobj = (_object_t*)data;
|
|
_clone_object(coll->ref.s, vobj, vtgt);
|
|
|
|
_set_dict(coll, 0, 0, ktgt, vtgt);
|
|
_REF(ktgt)
|
|
_REF(vtgt)
|
|
|
|
return 1;
|
|
}
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
#ifdef MB_ENABLE_CLASS
|
|
void _init_class(mb_interpreter_t* s, _class_t* instance, char* n) {
|
|
/* Initialize a class */
|
|
_running_context_t* running = 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);
|
|
}
|
|
|
|
void _begin_class(mb_interpreter_t* s) {
|
|
/* Begin parsing a class */
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
context = s->parsing_context;
|
|
context->class_state = _CLASS_STATE_PROC;
|
|
}
|
|
|
|
bool_t _end_class(mb_interpreter_t* s) {
|
|
/* End parsing a class */
|
|
_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;
|
|
}
|
|
|
|
void _unref_class(_ref_t* ref, void* data) {
|
|
/* Unreference a class instance */
|
|
if(ref->s->valid)
|
|
_out_of_scope(ref->s, ((_class_t*)data)->scope, (_class_t*)data, false);
|
|
|
|
if(!(*(ref->count)))
|
|
_destroy_class((_class_t*)data);
|
|
}
|
|
|
|
void _destroy_class(_class_t* c) {
|
|
/* Destroy a class instance */
|
|
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);
|
|
}
|
|
|
|
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) {
|
|
/* Traverse all fields of a class instance, and its meta class instances recursively as well */
|
|
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;
|
|
}
|
|
|
|
bool_t _link_meta_class(mb_interpreter_t* s, _class_t* derived, _class_t* base) {
|
|
/* Link a class instance to the meta list of another class instance */
|
|
mb_assert(s && derived && base);
|
|
|
|
if(_ls_find(derived->meta_list, base, (_ls_compare)_ht_cmp_intptr)) {
|
|
_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;
|
|
}
|
|
|
|
void _unlink_meta_class(mb_interpreter_t* s, _class_t* derived) {
|
|
/* Unlink all meta class instances of a class instance */
|
|
mb_assert(s && derived);
|
|
|
|
_LS_FOREACH(derived->meta_list, _do_nothing_on_object, _unlink_meta_instance, derived);
|
|
_ls_clear(derived->meta_list);
|
|
}
|
|
|
|
int _unlink_meta_instance(void* data, void* extra, _class_t* derived) {
|
|
/* Unlink a meta class instance */
|
|
_class_t* base = 0;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data && derived);
|
|
|
|
base = (_class_t*)data;
|
|
_unref(&base->ref, base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int _clone_clsss_field(void* data, void* extra, void* n) {
|
|
/* Clone fields of a class instance to another */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 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 = (_var_t*)obj->data.variable;
|
|
if(!_search_identifier_in_scope_chain(instance->ref.s, instance->scope, var->name, 0, 0, 0)) {
|
|
ret = _duplicate_parameter(var, 0, instance->scope);
|
|
_clone_object(instance->ref.s, obj, ret->data.variable->data);
|
|
}
|
|
|
|
break;
|
|
case _DT_ROUTINE:
|
|
sub = (_routine_t*)obj->data.routine;
|
|
if(!_search_identifier_in_scope_chain(instance->ref.s, instance->scope, sub->name, 0, 0, 0)) {
|
|
_routine_t* routine = (_routine_t*)mb_malloc(sizeof(_routine_t));
|
|
memset(routine, 0, sizeof(_routine_t));
|
|
routine->name = mb_strdup(sub->name, 0);
|
|
routine->instance = instance;
|
|
routine->is_cloned = true;
|
|
routine->type = sub->type;
|
|
routine->func = sub->func;
|
|
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;
|
|
}
|
|
|
|
bool_t _clone_class_meta_link(_class_t* meta, void* n, void* ret) {
|
|
/* Link meta class to a new instance */
|
|
_class_t* instance = (_class_t*)n;
|
|
mb_unrefvar(ret);
|
|
|
|
mb_assert(meta && n);
|
|
|
|
_link_meta_class(instance->ref.s, instance, meta);
|
|
|
|
return true;
|
|
}
|
|
|
|
int _search_class_meta_function(mb_interpreter_t* s, _class_t* instance, const char* n, _routine_t** f) {
|
|
/* Search for a meta function with a specific name and assign to a member field */
|
|
_ls_node_t* 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;
|
|
}
|
|
|
|
bool_t _is_class(_class_t* instance, void* m, void* ret) {
|
|
/* Detect whether a class instance is inherited from another */
|
|
_class_t* meta = (_class_t*)m;
|
|
bool_t* r = (bool_t*)ret;
|
|
|
|
mb_assert(instance && meta && ret);
|
|
|
|
*r = instance == meta;
|
|
|
|
return !(*r);
|
|
}
|
|
|
|
bool_t _add_class_meta_reachable(_class_t* meta, void* ht, void* ret) {
|
|
/* Add a meta class instance to a GC reachable table */
|
|
_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
|
|
int _reflect_class_field(void* data, void* extra, void* d) {
|
|
/* Reflect each field of a class instance to a dictionary */
|
|
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 */
|
|
|
|
_class_t* _reflect_string_to_class(mb_interpreter_t* s, const char* n, mb_value_t* arg) {
|
|
/* Reflect a class instance from a string */
|
|
_ls_node_t* cs = 0;
|
|
_object_t* c = 0;
|
|
|
|
cs = _search_identifier_in_scope_chain(s, 0, n, 0, 0, 0);
|
|
if(!cs || !cs->data) 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;
|
|
}
|
|
#endif /* MB_ENABLE_CLASS */
|
|
|
|
void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n, mb_routine_func_t f) {
|
|
/* Initialize a routine */
|
|
_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;
|
|
}
|
|
}
|
|
|
|
void _begin_routine(mb_interpreter_t* s) {
|
|
/* Begin parsing a routine */
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
context = s->parsing_context;
|
|
context->routine_state++;
|
|
}
|
|
|
|
bool_t _end_routine(mb_interpreter_t* s) {
|
|
/* End parsing a routine */
|
|
_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;
|
|
}
|
|
|
|
void _begin_routine_parameter_list(mb_interpreter_t* s) {
|
|
/* Begin parsing the parameter list of a routine */
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
context = s->parsing_context;
|
|
context->routine_params_state++;
|
|
}
|
|
|
|
void _end_routine_parameter_list(mb_interpreter_t* s) {
|
|
/* End parsing the parameter list of a routine */
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s);
|
|
|
|
context = s->parsing_context;
|
|
context->routine_params_state--;
|
|
}
|
|
|
|
_object_t* _duplicate_parameter(void* data, void* extra, _running_context_t* running) {
|
|
/* Duplicate a parameter from a parameter list to variable dictionary */
|
|
_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;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
_running_context_t* _init_lambda(mb_interpreter_t* s, _routine_t* routine) {
|
|
/* Initialize a lambda */
|
|
_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;
|
|
}
|
|
|
|
void _unref_routine(_ref_t* ref, void* data) {
|
|
/* Unreference a lambda routine */
|
|
if(!(*(ref->count)))
|
|
_destroy_routine(ref->s, (_routine_t*)data);
|
|
}
|
|
|
|
void _destroy_routine(mb_interpreter_t* s, _routine_t* r) {
|
|
/* Destroy a lambda routine */
|
|
if(r->name) {
|
|
safe_free(r->name);
|
|
}
|
|
if(!r->is_cloned) {
|
|
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)
|
|
_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);
|
|
}
|
|
|
|
void _mark_upvalue(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj, const char* n) {
|
|
/* Mark an upvalue of a lambda */
|
|
_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, 1, 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))
|
|
_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);
|
|
}
|
|
|
|
void _try_mark_upvalue(mb_interpreter_t* s, _routine_t* r, _object_t* obj) {
|
|
/* Try to mark upvalues of a lambda */
|
|
_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;
|
|
}
|
|
}
|
|
|
|
_running_context_ref_t* _create_outer_scope(mb_interpreter_t* s) {
|
|
/* Create an outer scope, which is a referenced type */
|
|
_running_context_ref_t* 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;
|
|
}
|
|
|
|
void _unref_outer_scope(_ref_t* ref, void* data) {
|
|
/* Unreference an outer scope */
|
|
if(!(*(ref->count)))
|
|
_destroy_outer_scope((_running_context_ref_t*)data);
|
|
}
|
|
|
|
void _destroy_outer_scope(_running_context_ref_t* p) {
|
|
/* Destroy an outer scope */
|
|
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);
|
|
mb_free(scope);
|
|
}
|
|
while(p) {
|
|
_running_context_ref_t* scope = p;
|
|
p = p->prev;
|
|
_unref(&scope->ref, scope);
|
|
}
|
|
}
|
|
|
|
int _do_nothing_on_ht_for_lambda(void* data, void* extra) {
|
|
/* Do nothing, this is a helper function for lambda */
|
|
int result = _OP_RESULT_NORMAL;
|
|
mb_unrefvar(data);
|
|
mb_unrefvar(extra);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _fill_with_upvalue(void* data, void* extra, void* p) {
|
|
/* Fill an outer scope with the original value */
|
|
_object_t* obj = (_object_t*)data;
|
|
const char* n = (const char*)extra;
|
|
_upvalue_scope_tuple_t* tuple = (_upvalue_scope_tuple_t*)p;
|
|
unsigned int ul = 0;
|
|
_ls_node_t* ast = 0;
|
|
|
|
_ls_node_t* 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, 1, 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);
|
|
_REF(var->data)
|
|
if(_IS_ROUTINE(obj) && obj->data.routine->type != _IT_LAMBDA) {
|
|
ovar->ref = true;
|
|
var->data->ref = true;
|
|
}
|
|
#ifdef MB_ENABLE_CLASS
|
|
var->pathing = 0;
|
|
#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;
|
|
}
|
|
|
|
int _remove_filled_upvalue(void* data, void* extra, void* u) {
|
|
/* Remove filled upvalues */
|
|
_ht_node_t* ht = (_ht_node_t*)u;
|
|
|
|
_ht_remove_exist(data, extra, ht);
|
|
|
|
return _OP_RESULT_NORMAL;
|
|
}
|
|
|
|
int _fill_outer_scope(void* data, void* extra, void* t) {
|
|
/* Fill an outer scope with the original one */
|
|
_lambda_t* lambda = (_lambda_t*)data;
|
|
_upvalue_scope_tuple_t* tuple = (_upvalue_scope_tuple_t*)t;
|
|
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;
|
|
}
|
|
|
|
_running_context_t* _link_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) {
|
|
/* Link the local scope of a lambda and all upvalue scopes in chain to a given scope */
|
|
_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;
|
|
}
|
|
|
|
_running_context_t* _unlink_lambda_scope_chain(mb_interpreter_t* s, _lambda_t* lambda, bool_t weak) {
|
|
/* Unlink the local scope of a lambda and all upvalue scopes in chain from a given scope */
|
|
_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;
|
|
}
|
|
|
|
bool_t _is_valid_lambda_body_node(mb_interpreter_t* s, _lambda_t* lambda, _object_t* obj) {
|
|
/* Check whether an object is a valid lambda body node */
|
|
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
|
|
_running_context_t* _reference_scope_by_class(mb_interpreter_t* s, _running_context_t* p, _class_t* c) {
|
|
/* Create a scope reference to an exist one by a class */
|
|
_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;
|
|
}
|
|
|
|
_running_context_t* _push_scope_by_class(mb_interpreter_t* s, _running_context_t* p) {
|
|
/* Push a scope by a class */
|
|
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;
|
|
}
|
|
|
|
_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) {
|
|
/* Try to search an identifire from a class */
|
|
_ls_node_t* result = 0;
|
|
_ls_node_t* node = 0;
|
|
_class_t* meta = 0;
|
|
|
|
mb_assert(s && instance && n);
|
|
|
|
result = _search_identifier_in_scope_chain(s, instance->scope, n, 0, ht, sp);
|
|
|
|
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;
|
|
}
|
|
|
|
_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) {
|
|
/* Try to search an identifier accessor in a scope */
|
|
_ls_node_t* result = 0;
|
|
_object_t* obj = 0;
|
|
char acc[_SINGLE_SYMBOL_MAX_LENGTH];
|
|
int i = 0;
|
|
int j = 0;
|
|
_class_t* instance = 0;
|
|
|
|
mb_assert(s && n);
|
|
|
|
while((i == 0) || (i > 0 && n[i - 1])) {
|
|
acc[j] = n[i];
|
|
if(_is_accessor(acc[j]) || acc[j] == '\0') {
|
|
acc[j] = '\0';
|
|
if(instance)
|
|
result = _search_identifier_in_class(s, instance, acc, ht, sp);
|
|
else
|
|
result = _search_identifier_in_scope_chain(s, scope, acc, 0, ht, sp);
|
|
if(!result) return 0;
|
|
obj = (_object_t*)result->data;
|
|
if(!obj) return 0;
|
|
switch(obj->type) {
|
|
case _DT_VAR:
|
|
if(obj->data.variable->data->type == _DT_CLASS)
|
|
instance = obj->data.variable->data->data.instance;
|
|
|
|
break;
|
|
case _DT_CLASS:
|
|
instance = obj->data.instance;
|
|
|
|
break;
|
|
case _DT_ROUTINE: /* Do nothing */
|
|
break;
|
|
default:
|
|
mb_assert(0 && "Unsupported.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
j = 0;
|
|
i++;
|
|
|
|
continue;
|
|
}
|
|
j++;
|
|
i++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_CLASS */
|
|
|
|
_running_context_t* _reference_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) {
|
|
/* Create a scope reference to an exist one by a routine */
|
|
_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;
|
|
}
|
|
|
|
_running_context_t* _push_weak_scope_by_routine(mb_interpreter_t* s, _running_context_t* p, _routine_t* r) {
|
|
/* Push a weak scope by a routine */
|
|
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;
|
|
}
|
|
|
|
_running_context_t* _push_scope_by_routine(mb_interpreter_t* s, _running_context_t* p) {
|
|
/* Push a scope by a routine */
|
|
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;
|
|
}
|
|
|
|
void _destroy_scope(mb_interpreter_t* s, _running_context_t* p) {
|
|
/* Destroy a scope */
|
|
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);
|
|
}
|
|
|
|
_running_context_t* _pop_weak_scope(mb_interpreter_t* s, _running_context_t* p) {
|
|
/* Pop a weak scope */
|
|
mb_assert(s);
|
|
|
|
if(p)
|
|
p->prev = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
_running_context_t* _pop_scope(mb_interpreter_t* s, bool_t tidy) {
|
|
/* Pop a scope */
|
|
_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, true);
|
|
|
|
return s->running_context;
|
|
}
|
|
|
|
void _out_of_scope(mb_interpreter_t* s, _running_context_t* running, void* instance, bool_t lose) {
|
|
/* Out of current scope */
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
_upvalue_scope_tuple_t tuple;
|
|
#endif /* MB_ENABLE_LAMBDA */
|
|
|
|
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 && running->var_dict)
|
|
_ht_foreach(running->var_dict, _lose_object);
|
|
}
|
|
|
|
_running_context_t* _find_scope(mb_interpreter_t* s, _running_context_t* p) {
|
|
/* Find a scope in a scope chain */
|
|
_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;
|
|
}
|
|
|
|
_running_context_t* _get_root_scope(_running_context_t* scope) {
|
|
/* Get the root scope in a scope chain */
|
|
_running_context_t* result = 0;
|
|
|
|
while(scope) {
|
|
result = scope;
|
|
scope = scope->prev;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef MB_ENABLE_LAMBDA
|
|
_running_context_ref_t* _get_root_ref_scope(_running_context_ref_t* scope) {
|
|
/* Get the root referenced scope in a referenced scope chain */
|
|
_running_context_ref_t* result = 0;
|
|
|
|
while(scope) {
|
|
result = scope;
|
|
scope = scope->prev;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_LAMBDA */
|
|
|
|
_running_context_t* _get_scope_to_add_routine(mb_interpreter_t* s) {
|
|
/* Get a proper scope to define a routine */
|
|
_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;
|
|
}
|
|
|
|
_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) {
|
|
/* Try to search an identifier in a scope chain */
|
|
_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);
|
|
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;
|
|
}
|
|
|
|
_array_t* _search_array_in_scope_chain(mb_interpreter_t* s, _array_t* i, _object_t** o) {
|
|
/* Try to search an array in a scope chain */
|
|
_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, 0, 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;
|
|
}
|
|
|
|
_var_t* _search_var_in_scope_chain(mb_interpreter_t* s, _var_t* i) {
|
|
/* Try to search a variable in a scope chain */
|
|
_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, 1, 0, 0);
|
|
if(scp) {
|
|
obj = (_object_t*)scp->data;
|
|
if(obj && obj->type == _DT_VAR)
|
|
result = obj->data.variable;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
_var_t* _create_var(_object_t** oobj, const char* n, size_t ns, bool_t dup_name) {
|
|
/* Create a variable object */
|
|
_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;
|
|
}
|
|
|
|
_object_t* _create_object(void) {
|
|
/* Create an _object_t struct */
|
|
_object_t* result = (_object_t*)mb_malloc(sizeof(_object_t));
|
|
_MAKE_NIL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _clone_object(mb_interpreter_t* s, _object_t* obj, _object_t* tgt) {
|
|
/* Clone the data of an object */
|
|
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);
|
|
|
|
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
|
|
);
|
|
_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:
|
|
tgt->data.array = obj->data.array;
|
|
mb_assert(0 && "Not implemented.");
|
|
|
|
break;
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
case _DT_LIST:
|
|
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);
|
|
|
|
break;
|
|
case _DT_LIST_IT:
|
|
tgt->data.list_it = _create_list_it(obj->data.list_it->list, true);
|
|
|
|
break;
|
|
case _DT_DICT:
|
|
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);
|
|
|
|
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:
|
|
tgt->data.instance = (_class_t*)mb_malloc(sizeof(_class_t));
|
|
_init_class(s, tgt->data.instance, mb_strdup(obj->data.instance->name, 0));
|
|
_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);
|
|
_pop_scope(s, false);
|
|
|
|
break;
|
|
#endif /* MB_ENABLE_CLASS */
|
|
case _DT_ROUTINE:
|
|
tgt->data.routine = obj->data.routine;
|
|
|
|
break;
|
|
case _DT_NIL: /* Fall through */
|
|
case _DT_UNKNOWN: /* Fall through */
|
|
case _DT_TYPE: /* Fall through */
|
|
case _DT_INT: /* Fall through */
|
|
case _DT_REAL: /* 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;
|
|
}
|
|
|
|
int _dispose_object(_object_t* obj) {
|
|
/* Dispose the data of an object */
|
|
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);
|
|
_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_TYPE: /* Fall through */
|
|
case _DT_INT: /* Fall through */
|
|
case _DT_REAL: /* 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;
|
|
}
|
|
|
|
int _destroy_object(void* data, void* extra) {
|
|
/* Destroy an object and its data */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
if(!_dispose_object(obj))
|
|
goto _exit;
|
|
safe_free(obj);
|
|
|
|
_exit:
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _destroy_object_with_extra(void* data, void* extra) {
|
|
/* Destroy an object, including its data and extra data */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_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:
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _destroy_object_not_compile_time(void* data, void* extra) {
|
|
/* Destroy an object which is not come from compile time */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_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:
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _destroy_object_capsule_only(void* data, void* extra) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
safe_free(obj);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _destroy_object_capsule_only_with_extra(void* data, void* extra) {
|
|
/* Destroy only the capsule (wrapper) of an object, leave the data behind, deal with extra as well */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_object_t* obj = 0;
|
|
|
|
mb_assert(data);
|
|
|
|
obj = (_object_t*)data;
|
|
safe_free(obj);
|
|
|
|
obj = (_object_t*)extra;
|
|
safe_free(obj);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _do_nothing_on_object(void* data, void* extra) {
|
|
/* Do nothing with an object, this is a helper function */
|
|
int result = _OP_RESULT_NORMAL;
|
|
mb_unrefvar(data);
|
|
mb_unrefvar(extra);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _lose_object(void* data, void* extra) {
|
|
/* Lose an object of of scope */
|
|
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;
|
|
}
|
|
|
|
int _remove_source_object(void* data, void* extra) {
|
|
/* Remove an object referenced to source code */
|
|
int result = _OP_RESULT_DEL_NODE;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _destroy_memory(void* data, void* extra) {
|
|
/* Destroy a chunk of memory */
|
|
int result = _OP_RESULT_NORMAL;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
safe_free(data);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _compare_numbers(const _object_t* first, const _object_t* second) {
|
|
/* Compare two numbers from two objects */
|
|
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;
|
|
}
|
|
|
|
bool_t _is_internal_object(_object_t* obj) {
|
|
/* Determine whether an object is internal */
|
|
bool_t result = false;
|
|
|
|
mb_assert(obj);
|
|
|
|
result = (_exp_assign == obj) ||
|
|
(_OBJ_BOOL_TRUE == obj) || (_OBJ_BOOL_FALSE == obj);
|
|
|
|
return result;
|
|
}
|
|
|
|
_data_e _public_type_to_internal_type(mb_data_e t) {
|
|
/* Convert a public mb_data_e type to an internal _data_e */
|
|
switch(t) {
|
|
case MB_DT_NIL:
|
|
return _DT_NIL;
|
|
case MB_DT_TYPE:
|
|
return _DT_TYPE;
|
|
case MB_DT_INT:
|
|
return _DT_INT;
|
|
case MB_DT_REAL:
|
|
return _DT_REAL;
|
|
case MB_DT_STRING:
|
|
return _DT_STRING;
|
|
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;
|
|
}
|
|
}
|
|
|
|
mb_data_e _internal_type_to_public_type(_data_e t) {
|
|
/* Convert an internal mb_data_e type to a public _data_e */
|
|
switch(t) {
|
|
case _DT_NIL:
|
|
return MB_DT_NIL;
|
|
case _DT_TYPE:
|
|
return MB_DT_TYPE;
|
|
case _DT_INT:
|
|
return MB_DT_INT;
|
|
case _DT_REAL:
|
|
return MB_DT_REAL;
|
|
case _DT_STRING:
|
|
return MB_DT_STRING;
|
|
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;
|
|
}
|
|
}
|
|
|
|
int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn) {
|
|
/* Assign a public mb_value_t to an internal _object_t */
|
|
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_TYPE:
|
|
itn->type = _DT_TYPE;
|
|
itn->data.type = pbl->value.type;
|
|
|
|
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_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;
|
|
}
|
|
|
|
int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl) {
|
|
/* Assign an internal _object_t to a public mb_value_t */
|
|
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_TYPE:
|
|
pbl->type = MB_DT_TYPE;
|
|
pbl->value.type = itn->data.type;
|
|
|
|
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_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;
|
|
}
|
|
|
|
int _create_internal_object_from_public_value(mb_value_t* pbl, _object_t** itn) {
|
|
/* Create an internal object from a public value */
|
|
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;
|
|
}
|
|
|
|
int _compare_public_value_and_internal_object(mb_value_t* pbl, _object_t* itn) {
|
|
/* Compare a public value and an internal object */
|
|
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 = _cmp_bytes(pbl->value.bytes, tmp.value.bytes);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void _try_clear_intermediate_value(void* data, void* extra, mb_interpreter_t* s) {
|
|
/* Try clear the intermediate value */
|
|
_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);
|
|
}
|
|
}
|
|
|
|
void _destroy_lazy_objects(mb_interpreter_t* s) {
|
|
/* Destroy lazy releasing objects */
|
|
_ls_foreach(s->lazy_destroy_objects, _destroy_object);
|
|
_ls_clear(s->lazy_destroy_objects);
|
|
}
|
|
|
|
void _mark_lazy_destroy_string(mb_interpreter_t* s, char* ch) {
|
|
/* Mark a string as lazy releasing destroy */
|
|
_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);
|
|
}
|
|
|
|
void _assign_public_value(mb_value_t* tgt, mb_value_t* src) {
|
|
/* Assign a value with another */
|
|
_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;
|
|
}
|
|
|
|
void _swap_public_value(mb_value_t* tgt, mb_value_t* src) {
|
|
/* Swap two public values */
|
|
mb_value_t tmp;
|
|
|
|
mb_assert(tgt && src);
|
|
|
|
tmp = *tgt;
|
|
*tgt = *src;
|
|
*src = tmp;
|
|
}
|
|
|
|
int _clear_scope_chain(mb_interpreter_t* s) {
|
|
/* Clear the scope chain */
|
|
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;
|
|
}
|
|
|
|
int _dispose_scope_chain(mb_interpreter_t* s) {
|
|
/* Dispose the scope chain */
|
|
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;
|
|
}
|
|
|
|
void _tidy_scope_chain(mb_interpreter_t* s) {
|
|
/* Tidy the scope chain */
|
|
_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 */
|
|
}
|
|
|
|
void _tidy_intermediate_value(_ref_t* ref, void* data) {
|
|
/* Tidy the intermediate value */
|
|
_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
_object_t* _eval_var_in_print(mb_interpreter_t* s, _object_t** val_ptr, _ls_node_t** ast, _object_t* obj) {
|
|
/* Evaluate a variable, this is a helper function for the PRINT statement */
|
|
_object_t tmp;
|
|
|
|
if(obj->type == _DT_ROUTINE) {
|
|
_execute_statement(s, ast);
|
|
_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;
|
|
} else if(obj->type == _DT_VAR) {
|
|
*val_ptr = obj->data.variable->data;
|
|
if(*ast) *ast = (*ast)->next;
|
|
} else {
|
|
*val_ptr = obj;
|
|
if(*ast) *ast = (*ast)->next;
|
|
}
|
|
|
|
return *val_ptr;
|
|
}
|
|
|
|
void _stepped(mb_interpreter_t* s, _ls_node_t* ast) {
|
|
/* Callback a stepped debug handler, this function is called each step */
|
|
_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, obj->source_pos, obj->source_row, obj->source_col);
|
|
#else /* MB_ENABLE_SOURCE_TRACE */
|
|
s->debug_stepped_handler(s, obj->source_pos, 0, 0);
|
|
#endif /* MB_ENABLE_SOURCE_TRACE */
|
|
} else {
|
|
s->debug_stepped_handler(s, -1, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int _execute_statement(mb_interpreter_t* s, _ls_node_t** l) {
|
|
/* Execute the ast, this is the core execution function */
|
|
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:
|
|
#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->pathing) {
|
|
/* Need to path */
|
|
_ls_node_t* pathed = _search_identifier_in_scope_chain(s, 0, obj->data.variable->name, 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 {
|
|
mb_assert(0 && "Impossible.");
|
|
}
|
|
} else 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 {
|
|
/* 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 = ast ? (_object_t*)ast->data : 0;
|
|
if(!obj) {
|
|
/* Do nothing */
|
|
} else if(_IS_EOS(obj)) {
|
|
ast = ast->next;
|
|
} else if(_IS_SEP(obj, ':')) {
|
|
skip_to_eoi = false;
|
|
ast = ast->next;
|
|
} else if(obj && obj->type == _DT_VAR) {
|
|
_handle_error_on_obj(s, SE_RN_COLON_EXPECTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
} 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;
|
|
}
|
|
|
|
int _common_end_looping(mb_interpreter_t* s, _ls_node_t** l) {
|
|
/* Common function to end current looping */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
if(_skip_struct(s, l, _core_for, _core_next) == MB_FUNC_OK)
|
|
_skip_to(s, l, 0, _DT_EOS);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _common_keep_looping(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) {
|
|
/* Common function to keep current looping */
|
|
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);
|
|
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);
|
|
result = MB_FUNC_OK;
|
|
|
|
goto _exit;
|
|
} else if(result != MB_FUNC_OK && result != MB_SUB_RETURN) { /* 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;
|
|
}
|
|
|
|
int _execute_normal_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) {
|
|
/* Execute normal FOR-TO-STEP-NEXT-routine */
|
|
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);
|
|
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, 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, 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 */
|
|
_common_end_looping(s, &ast);
|
|
|
|
goto _exit;
|
|
} else {
|
|
/* Keep looping */
|
|
_common_keep_looping(s, &ast, var_loop);
|
|
|
|
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
|
|
int _execute_ranged_for_loop(mb_interpreter_t* s, _ls_node_t** l, _var_t* var_loop) {
|
|
/* Execute ranged FOR-IN-NEXT-routine */
|
|
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, 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 */
|
|
_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 */
|
|
_common_keep_looping(s, &ast, var_loop);
|
|
|
|
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 */
|
|
|
|
int _skip_to(mb_interpreter_t* s, _ls_node_t** l, mb_func_t f, _data_e t) {
|
|
/* Skip current execution flow to a specific function */
|
|
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, 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;
|
|
}
|
|
|
|
int _skip_if_chunk(mb_interpreter_t* s, _ls_node_t** l) {
|
|
/* Skip current IF execution flow to next chunk */
|
|
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, s->source_file, TON(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, TON(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_STRUCTURE_NOT_COMPLETED, s->source_file, TON(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;
|
|
}
|
|
|
|
int _skip_struct(mb_interpreter_t* s, _ls_node_t** l, mb_func_t open_func, mb_func_t close_func) {
|
|
/* Skip current structure */
|
|
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 = (_ls_node_t*)*l;
|
|
|
|
count = 1;
|
|
do {
|
|
if(!ast->next) {
|
|
_handle_error_on_obj(s, SE_RN_STRUCTURE_NOT_COMPLETED, 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;
|
|
}
|
|
|
|
_running_context_t* _create_running_context(bool_t create_var_dict) {
|
|
/* Create a running context */
|
|
_running_context_t* 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;
|
|
}
|
|
|
|
_parsing_context_t* _reset_parsing_context(_parsing_context_t* context) {
|
|
/* Reset the parsing context of a MY-BASIC environment */
|
|
_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;
|
|
|
|
return context;
|
|
}
|
|
|
|
void _destroy_parsing_context(_parsing_context_t** context) {
|
|
/* Destroy the parsing context of a MY-BASIC environment */
|
|
if(!context || !(*context))
|
|
return;
|
|
|
|
if(*context) {
|
|
if((*context)->imported) {
|
|
_ls_foreach((*context)->imported, _destroy_memory);
|
|
_ls_destroy((*context)->imported);
|
|
}
|
|
safe_free(*context);
|
|
}
|
|
}
|
|
|
|
#ifdef MB_ENABLE_MODULE
|
|
_module_func_t* _create_module_func(mb_interpreter_t* s, mb_func_t f) {
|
|
/* Create a module function structure */
|
|
_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;
|
|
}
|
|
|
|
int _ls_destroy_module_func(void* data, void* extra) {
|
|
/* Destroy a module function structure */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_module_func_t* mod = 0;
|
|
mb_unrefvar(extra);
|
|
|
|
mb_assert(data);
|
|
|
|
mod = (_module_func_t*)data;
|
|
safe_free(mod->module);
|
|
safe_free(mod);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _ht_destroy_module_func_list(void* data, void* extra) {
|
|
/* Destroy all module function structures */
|
|
int result = _OP_RESULT_NORMAL;
|
|
_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);
|
|
|
|
result = _OP_RESULT_DEL_NODE;
|
|
|
|
return result;
|
|
}
|
|
#endif /* MB_ENABLE_MODULE */
|
|
|
|
char* _generate_func_name(mb_interpreter_t* s, char* n, bool_t with_mod) {
|
|
/* Generate a function name to be registered according to module information */
|
|
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;
|
|
}
|
|
|
|
int _register_func(mb_interpreter_t* s, char* n, mb_func_t f, bool_t local) {
|
|
/* Register a function to a MY-BASIC environment */
|
|
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);
|
|
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;
|
|
}
|
|
|
|
int _remove_func(mb_interpreter_t* s, char* n, bool_t local) {
|
|
/* Remove a function from a MY-BASIC environment */
|
|
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);
|
|
if(tmp)
|
|
_ls_remove(exists, tmp, _ls_destroy_module_func);
|
|
}
|
|
}
|
|
#endif /* MB_ENABLE_MODULE */
|
|
|
|
safe_free(n);
|
|
|
|
return result;
|
|
}
|
|
|
|
_ls_node_t* _find_func(mb_interpreter_t* s, char* n, bool_t* mod) {
|
|
/* Find function interface in the function dictionaries */
|
|
_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))
|
|
break;
|
|
result = result->next;
|
|
}
|
|
*mod = true;
|
|
}
|
|
}
|
|
#endif /* MB_ENABLE_MODULE */
|
|
|
|
safe_free(n);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _open_constant(mb_interpreter_t* s) {
|
|
/* Open global constants */
|
|
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, "TRUE", _OBJ_BOOL_TRUE);
|
|
mb_assert(ul);
|
|
ul = _ht_set_or_insert(running->var_dict, "FALSE", _OBJ_BOOL_FALSE);
|
|
mb_assert(ul);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _close_constant(mb_interpreter_t* s) {
|
|
/* Close global constants */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _open_core_lib(mb_interpreter_t* s) {
|
|
/* Open the core function library */
|
|
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;
|
|
}
|
|
|
|
int _close_core_lib(mb_interpreter_t* s) {
|
|
/* Close the core function library */
|
|
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;
|
|
}
|
|
|
|
int _open_std_lib(mb_interpreter_t* s) {
|
|
/* Open the standard function library */
|
|
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;
|
|
}
|
|
|
|
int _close_std_lib(mb_interpreter_t* s) {
|
|
/* Close the standard function library */
|
|
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
|
|
int _open_coll_lib(mb_interpreter_t* s) {
|
|
/* Open the collection function library */
|
|
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;
|
|
}
|
|
|
|
int _close_coll_lib(mb_interpreter_t* s) {
|
|
/* Close the collection function library */
|
|
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
|
|
*/
|
|
|
|
unsigned int mb_ver(void) {
|
|
/* Get the version number of this MY-BASIC system */
|
|
return _MB_VERSION;
|
|
}
|
|
|
|
const char* mb_ver_string(void) {
|
|
/* Get the version text of this MY-BASIC system */
|
|
return _MB_VERSION_STRING;
|
|
}
|
|
|
|
int mb_init(void) {
|
|
/* Initialize the MY-BASIC system */
|
|
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("#") + 1);
|
|
memcpy(_exp_assign->data.func->name, "#", strlen("#") + 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, "TRUE", strlen("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, "FALSE", strlen("FALSE") + 1, true);
|
|
bvar->data->type = _DT_INT;
|
|
bvar->data->data.integer = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_dispose(void) {
|
|
/* Close the MY-BASIC system */
|
|
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;
|
|
}
|
|
|
|
int mb_open(struct mb_interpreter_t** s) {
|
|
/* Open a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
_ht_node_t* local_scope = 0;
|
|
_ht_node_t* global_scope = 0;
|
|
_running_context_t* running = 0;
|
|
|
|
*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)->temp_values = _ls_create();
|
|
(*s)->lazy_destroy_objects = _ls_create();
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
(*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);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
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;
|
|
}
|
|
|
|
int mb_close(struct mb_interpreter_t** s) {
|
|
/* Close a MY-BASIC environment */
|
|
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);
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
_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;
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
_ls_foreach((*s)->temp_values, _destroy_object);
|
|
_ls_destroy((*s)->temp_values);
|
|
_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;
|
|
}
|
|
|
|
int mb_reset(struct mb_interpreter_t** s, bool_t clrf/* = false*/) {
|
|
/* Reset a MY-BASIC environment */
|
|
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;
|
|
}
|
|
|
|
int mb_register_func(struct mb_interpreter_t* s, const char* n, mb_func_t f) {
|
|
/* Register an API function to a MY-BASIC environment */
|
|
return _register_func(s, (char*)n, f, false);
|
|
}
|
|
|
|
int mb_remove_func(struct mb_interpreter_t* s, const char* n) {
|
|
/* Remove an API function from a MY-BASIC environment */
|
|
return _remove_func(s, (char*)n, false);
|
|
}
|
|
|
|
int mb_remove_reserved_func(struct mb_interpreter_t* s, const char* n) {
|
|
/* Remove a reserved API from a MY-BASIC environment */
|
|
return _remove_func(s, (char*)n, true);
|
|
}
|
|
|
|
int mb_begin_module(struct mb_interpreter_t* s, const char* n) {
|
|
/* Begin a module, all functions registered within a module will put inside it */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && n);
|
|
|
|
#ifdef MB_ENABLE_MODULE
|
|
s->with_module = mb_strdup(n, strlen(n) + 1);
|
|
|
|
goto _exit; /* Avoid an unreferenced label warning */
|
|
#else /* MB_ENABLE_MODULE */
|
|
_handle_error_on_obj(s, SE_RN_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result);
|
|
#endif /* MB_ENABLE_MODULE */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_end_module(struct mb_interpreter_t* s) {
|
|
/* End a module */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
#ifdef MB_ENABLE_MODULE
|
|
if(s->with_module) {
|
|
safe_free(s->with_module);
|
|
}
|
|
|
|
goto _exit; /* Avoid an unreferenced label warning */
|
|
#else /* MB_ENABLE_MODULE */
|
|
_handle_error_on_obj(s, SE_RN_NOT_SUPPORTED, s->source_file, (_object_t*)0, MB_FUNC_WARNING, _exit, result);
|
|
#endif /* MB_ENABLE_MODULE */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_attempt_func_begin(struct mb_interpreter_t* s, void** l) {
|
|
/* Try attempting to begin an API function */
|
|
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_STRUCTURE_NOT_COMPLETED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
ast = ast->next;
|
|
|
|
++s->no_eat_comma_mark;
|
|
|
|
_exit:
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_attempt_func_end(struct mb_interpreter_t* s, void** l) {
|
|
/* Try attempting to end an API function */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
--s->no_eat_comma_mark;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_attempt_open_bracket(struct mb_interpreter_t* s, void** l) {
|
|
/* Try attempting an open bracket */
|
|
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;
|
|
}
|
|
|
|
int mb_attempt_close_bracket(struct mb_interpreter_t* s, void** l) {
|
|
/* Try attempting a close bracket */
|
|
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;
|
|
}
|
|
|
|
int mb_has_arg(struct mb_interpreter_t* s, void** l) {
|
|
/* Detect if there is any more argument */
|
|
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;
|
|
}
|
|
|
|
int mb_pop_int(struct mb_interpreter_t* s, void** l, int_t* val) {
|
|
/* Pop an integer argument */
|
|
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;
|
|
}
|
|
|
|
int mb_pop_real(struct mb_interpreter_t* s, void** l, real_t* val) {
|
|
/* Pop a float point argument */
|
|
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;
|
|
}
|
|
|
|
int mb_pop_string(struct mb_interpreter_t* s, void** l, char** val) {
|
|
/* Pop a string argument */
|
|
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;
|
|
}
|
|
|
|
int mb_pop_usertype(struct mb_interpreter_t* s, void** l, void** val) {
|
|
/* Pop a usertype argument */
|
|
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;
|
|
}
|
|
|
|
int mb_pop_value(struct mb_interpreter_t* s, void** l, mb_value_t* val) {
|
|
/* Pop an argument value */
|
|
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) {
|
|
_ls_foreach(s->temp_values, _destroy_object);
|
|
_ls_clear(s->temp_values);
|
|
|
|
val_ptr = _create_object();
|
|
memcpy(val_ptr, &val_obj, sizeof(_object_t));
|
|
_ls_pushback(s->temp_values, val_ptr);
|
|
}
|
|
_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;
|
|
}
|
|
|
|
int mb_push_int(struct mb_interpreter_t* s, void** l, int_t val) {
|
|
/* Push an integer argument */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
|
|
mb_assert(s && l);
|
|
|
|
arg.type = MB_DT_INT;
|
|
arg.value.integer = val;
|
|
mb_check(mb_push_value(s, l, arg));
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_push_real(struct mb_interpreter_t* s, void** l, real_t val) {
|
|
/* Push a float point argument */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
|
|
mb_assert(s && l);
|
|
|
|
arg.type = MB_DT_REAL;
|
|
arg.value.float_point = val;
|
|
mb_convert_to_int_if_posible(arg);
|
|
mb_check(mb_push_value(s, l, arg));
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_push_string(struct mb_interpreter_t* s, void** l, char* val) {
|
|
/* Push a string argument */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
|
|
mb_assert(s && l);
|
|
|
|
arg.type = MB_DT_STRING;
|
|
arg.value.string = val;
|
|
mb_check(mb_push_value(s, l, arg));
|
|
_mark_lazy_destroy_string(s, val);
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_push_usertype(struct mb_interpreter_t* s, void** l, void* val) {
|
|
/* Push a usertype argument */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
|
|
mb_assert(s && l);
|
|
|
|
arg.type = MB_DT_USERTYPE;
|
|
arg.value.usertype = val;
|
|
mb_check(mb_push_value(s, l, arg));
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_push_value(struct mb_interpreter_t* s, void** l, mb_value_t val) {
|
|
/* Push an argument value */
|
|
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)
|
|
|
|
#ifdef MB_ENABLE_GC
|
|
_gc_try_trigger(s);
|
|
#endif /* MB_ENABLE_GC */
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_begin_class(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t** meta, int c, mb_value_t* out) {
|
|
/* Begin a class */
|
|
#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, 0, 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, TON(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, TON(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 */
|
|
}
|
|
|
|
int mb_end_class(struct mb_interpreter_t* s, void** l) {
|
|
/* End a class */
|
|
#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 */
|
|
}
|
|
|
|
int mb_get_class_userdata(struct mb_interpreter_t* s, void** l, void** d) {
|
|
/* Get the userdata of a class instance */
|
|
#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, TON(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 */
|
|
}
|
|
|
|
int mb_set_class_userdata(struct mb_interpreter_t* s, void** l, void* d) {
|
|
/* Set the userdata of a class instance */
|
|
#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, TON(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 */
|
|
}
|
|
|
|
int mb_get_value_by_name(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) {
|
|
/* Get a value by its identifier name */
|
|
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, 1, 0, 0);
|
|
if(tmp && tmp->data) {
|
|
obj = (_object_t*)tmp->data;
|
|
_internal_object_to_public_value(obj, val);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_add_var(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t val, bool_t force) {
|
|
/* Add a variable with a specific name */
|
|
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, TON(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;
|
|
}
|
|
|
|
int mb_get_var(struct mb_interpreter_t* s, void** l, void** v) {
|
|
/* Get a token literally, store it in an argument if it's a variable */
|
|
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;
|
|
}
|
|
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_get_var_value(struct mb_interpreter_t* s, void* v, mb_value_t* val) {
|
|
/* Get the value of a variable */
|
|
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;
|
|
}
|
|
|
|
int mb_set_var_value(struct mb_interpreter_t* s, void* v, mb_value_t val) {
|
|
/* Set the value of a variable */
|
|
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;
|
|
}
|
|
|
|
int mb_init_array(struct mb_interpreter_t* s, void** l, mb_data_e t, int* d, int c, void** a) {
|
|
/* Create an array */
|
|
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;
|
|
#ifdef MB_SIMPLE_ARRAY
|
|
if(t == MB_DT_REAL) {
|
|
type = _DT_REAL;
|
|
} else if(t == MB_DT_STRING) {
|
|
type = _DT_STRING;
|
|
} else {
|
|
result = MB_NEED_COMPLEX_ARRAY;
|
|
|
|
goto _exit;
|
|
}
|
|
#else /* MB_SIMPLE_ARRAY */
|
|
type = _DT_REAL;
|
|
#endif /* MB_SIMPLE_ARRAY */
|
|
|
|
arr = _create_array(0, type, s);
|
|
for(j = 0; j < c; j++) {
|
|
n = d[j];
|
|
arr->dimensions[arr->dimension_count++] = n;
|
|
if(arr->count)
|
|
arr->count *= n;
|
|
else
|
|
arr->count += n;
|
|
}
|
|
_init_array(arr);
|
|
if(!arr->raw) {
|
|
arr->dimension_count = 0;
|
|
arr->dimensions[0] = 0;
|
|
arr->count = 0;
|
|
}
|
|
*a = arr;
|
|
|
|
goto _exit; /* Avoid an unreferenced label warning */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_get_array_len(struct mb_interpreter_t* s, void** l, void* a, int r, int* i) {
|
|
/* Get the length of an array */
|
|
int result = MB_FUNC_OK;
|
|
_array_t* arr = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
arr = (_array_t*)a;
|
|
if(r < 0 || r >= arr->dimension_count) {
|
|
result = MB_RANK_OUT_OF_BOUNDS;
|
|
|
|
goto _exit;
|
|
}
|
|
if(i)
|
|
*i = arr->dimensions[r];
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_get_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t* val) {
|
|
/* Get an element of an array with a specific index */
|
|
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) {
|
|
result = MB_RANK_OUT_OF_BOUNDS;
|
|
|
|
goto _exit;
|
|
}
|
|
if(!val)
|
|
goto _exit;
|
|
|
|
index = _get_array_pos(s, arr, d, c);
|
|
if(index < 0) {
|
|
result = MB_INDEX_OUT_OF_BOUNDS;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
_get_array_elem(s, arr, index, &val->value, &type);
|
|
val->type = _internal_type_to_public_type(type);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_set_array_elem(struct mb_interpreter_t* s, void** l, void* a, int* d, int c, mb_value_t val) {
|
|
/* Set an element of an array with a specific index */
|
|
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) {
|
|
result = MB_RANK_OUT_OF_BOUNDS;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
index = _get_array_pos(s, arr, d, c);
|
|
if(index < 0) {
|
|
result = MB_INDEX_OUT_OF_BOUNDS;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
type = _public_type_to_internal_type(val.type);
|
|
_set_array_elem(s, 0, arr, (unsigned int)index, &val.value, &type);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_init_coll(struct mb_interpreter_t* s, void** l, mb_value_t* coll) {
|
|
/* Initialize a collection */
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
#else /* MB_ENABLE_COLLECTION_LIB */
|
|
mb_unrefvar(coll);
|
|
_handle_error_on_obj(s, SE_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_get_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t* val) {
|
|
/* Get an element of a collection */
|
|
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, TON(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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(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_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
_exit:
|
|
if(val) *val = ret;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx, mb_value_t val) {
|
|
/* Set an element of a collection */
|
|
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, TON(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, TON(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_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_remove_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, mb_value_t idx) {
|
|
/* Remove an element from a collection */
|
|
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, TON(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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(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_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_count_coll(struct mb_interpreter_t* s, void** l, mb_value_t coll, int* c) {
|
|
/* Tell the element count of a collection */
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
#else /* MB_ENABLE_COLLECTION_LIB */
|
|
mb_unrefvar(coll);
|
|
_handle_error_on_obj(s, SE_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_COLLECTION_LIB */
|
|
|
|
_exit:
|
|
if(c) *c = ret;
|
|
|
|
return result;
|
|
}
|
|
|
|
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) {
|
|
/* Create a referenced usertype value */
|
|
#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 */
|
|
}
|
|
|
|
int mb_get_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val, void** out) {
|
|
/* Get the data of a referenced usertype value */
|
|
#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, TON(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 */
|
|
}
|
|
|
|
int mb_ref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) {
|
|
/* Increase the reference of a value by 1 */
|
|
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_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_REF(&obj);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_unref_value(struct mb_interpreter_t* s, void** l, mb_value_t val) {
|
|
/* Decrease the reference of a value by 1 */
|
|
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_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_UNREF(&obj);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_dispose_value(struct mb_interpreter_t* s, mb_value_t val) {
|
|
/* Dispose a value */
|
|
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;
|
|
}
|
|
|
|
int mb_get_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_value_t* val) {
|
|
/* Get a sub routine with a specific name */
|
|
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, 0, 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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int mb_set_routine(struct mb_interpreter_t* s, void** l, const char* n, mb_routine_func_t f, bool_t force) {
|
|
/* Set a sub routine with a specific name and native function pointer */
|
|
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, TON(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;
|
|
}
|
|
|
|
int mb_eval_routine(struct mb_interpreter_t* s, void** l, mb_value_t val, mb_value_t* args, unsigned argc) {
|
|
/* Evaluate a sub routine */
|
|
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, TON(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;
|
|
}
|
|
|
|
int mb_load_string(struct mb_interpreter_t* s, const char* l) {
|
|
/* Load and parse a script string */
|
|
int result = MB_FUNC_OK;
|
|
char ch = 0;
|
|
int status = 0;
|
|
int i = 0;
|
|
unsigned short _row = 0;
|
|
unsigned short _col = 0;
|
|
char wrapped = '\0';
|
|
_parsing_context_t* context = 0;
|
|
|
|
mb_assert(s && s->parsing_context);
|
|
|
|
context = s->parsing_context;
|
|
|
|
while(l[i]) {
|
|
ch = l[i];
|
|
if((ch == '\n' || ch == '\r') && (!wrapped || wrapped == ch)) {
|
|
wrapped = ch;
|
|
++context->parsing_row;
|
|
context->parsing_col = 0;
|
|
} else {
|
|
wrapped = '\0';
|
|
++context->parsing_col;
|
|
}
|
|
status = _parse_char(s, ch, 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;
|
|
++i;
|
|
++context->parsing_pos;
|
|
};
|
|
status = _parse_char(s, MB_EOS, context->parsing_pos, context->parsing_row, context->parsing_col);
|
|
|
|
_exit:
|
|
context->parsing_state = _PS_NORMAL;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_load_file(struct mb_interpreter_t* s, const char* f) {
|
|
/* Load and parse a script file */
|
|
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);
|
|
safe_free(buf);
|
|
|
|
if(result)
|
|
goto _exit;
|
|
} else {
|
|
_set_current_error(s, SE_PS_FILE_OPEN_FAILED, 0);
|
|
|
|
++result;
|
|
}
|
|
|
|
_exit:
|
|
context->parsing_state = _PS_NORMAL;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_run(struct mb_interpreter_t* s) {
|
|
/* Run the current AST */
|
|
int result = MB_FUNC_OK;
|
|
_ls_node_t* ast = 0;
|
|
|
|
_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;
|
|
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);
|
|
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:
|
|
_ls_foreach(s->temp_values, _destroy_object);
|
|
_ls_clear(s->temp_values);
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_suspend(struct mb_interpreter_t* s, void** l) {
|
|
/* Suspend current execution and save the context */
|
|
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;
|
|
}
|
|
|
|
int mb_schedule_suspend(struct mb_interpreter_t* s, int t) {
|
|
/* Schedule to suspend current execution */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
if(t == MB_FUNC_OK)
|
|
t = MB_FUNC_SUSPEND;
|
|
s->schedule_suspend_tag = t;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_debug_get(struct mb_interpreter_t* s, const char* n, mb_value_t* val) {
|
|
/* Get the value of an identifier */
|
|
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, 0, 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);
|
|
}
|
|
result = MB_DEBUG_ID_NOT_FOUND;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_debug_set(struct mb_interpreter_t* s, const char* n, mb_value_t val) {
|
|
/* Set the value of an identifier */
|
|
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, 0, 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 {
|
|
result = MB_DEBUG_ID_NOT_FOUND;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_debug_get_stack_trace(struct mb_interpreter_t* s, void** l, char** fs, unsigned fc) {
|
|
/* Get stack frame names of a MY-BASIC environment */
|
|
#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) {
|
|
f = s->stack_frames->prev;
|
|
while(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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
#endif /* MB_ENABLE_STACK_TRACE */
|
|
}
|
|
|
|
int mb_debug_set_stepped_handler(struct mb_interpreter_t* s, mb_debug_stepped_handler_t h) {
|
|
/* Set a stepped handler to a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
s->debug_stepped_handler = h;
|
|
|
|
return result;
|
|
}
|
|
|
|
const char* mb_get_type_string(mb_data_e t) {
|
|
/* Get type description text */
|
|
switch(t) {
|
|
case MB_DT_NIL:
|
|
return "NIL";
|
|
case MB_DT_UNKNOWN:
|
|
return "UNKNOWN";
|
|
case MB_DT_TYPE:
|
|
return "TYPE";
|
|
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_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";
|
|
#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 "";
|
|
}
|
|
}
|
|
|
|
int mb_raise_error(struct mb_interpreter_t* s, void** l, mb_error_e err, int ret) {
|
|
/* Raise an error */
|
|
int result = MB_FUNC_ERR;
|
|
|
|
mb_assert(s);
|
|
|
|
_handle_error_on_obj(s, err, s->source_file, TON(l), ret, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
mb_error_e mb_get_last_error(struct mb_interpreter_t* s) {
|
|
/* Get the last error information */
|
|
mb_error_e result = SE_NO_ERR;
|
|
|
|
mb_assert(s);
|
|
|
|
result = s->last_error;
|
|
s->last_error = SE_NO_ERR;
|
|
|
|
return result;
|
|
}
|
|
|
|
const char* mb_get_error_desc(mb_error_e err) {
|
|
/* Get the error description text */
|
|
if(err < _countof(_ERR_DESC))
|
|
return _ERR_DESC[err];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mb_set_error_handler(struct mb_interpreter_t* s, mb_error_handler_t h) {
|
|
/* Set an error handler to a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
s->error_handler = h;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_printer(struct mb_interpreter_t* s, mb_print_func_t p) {
|
|
/* Set a print functor to a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
s->printer = p;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_inputer(struct mb_interpreter_t* s, mb_input_func_t p) {
|
|
/* Set an input functor to a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
s->inputer = p;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_get_userdata(struct mb_interpreter_t* s, void** d) {
|
|
/* Get the userdata of a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && d);
|
|
|
|
if(s && d)
|
|
*d = s->userdata;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_userdata(struct mb_interpreter_t* s, void* d) {
|
|
/* Set the userdata of a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
if(s)
|
|
s->userdata = d;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_import_handler(struct mb_interpreter_t* s, mb_import_handler_t h) {
|
|
/* Set an import handler to a MY-BASIC environment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s);
|
|
|
|
s->import_handler = h;
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_gets(char* buf, int s) {
|
|
/* Safe stdin reader function */
|
|
int result = 0;
|
|
|
|
if(fgets(buf, s, stdin) == 0) {
|
|
fprintf(stderr, "Error reading.\n");
|
|
exit(1);
|
|
}
|
|
result = (int)strlen(buf);
|
|
if(buf[result - 1] == '\n')
|
|
buf[result - 1] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
char* mb_memdup(const char* val, unsigned size) {
|
|
/* Duplicate a string for internal use */
|
|
char* result = 0;
|
|
|
|
if(val != 0) {
|
|
result = (char*)mb_malloc(size);
|
|
memcpy(result, val, size);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mb_set_memory_manager(mb_memory_allocate_func_t a, mb_memory_free_func_t f) {
|
|
/* Register an allocator and a freer globally */
|
|
_mb_allocate_func = a;
|
|
_mb_free_func = f;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ========================================================} */
|
|
|
|
/*
|
|
** {========================================================
|
|
** Lib definitions
|
|
*/
|
|
|
|
/** Core lib */
|
|
int _core_dummy_assign(mb_interpreter_t* s, void** l) {
|
|
/* Operator #, dummy assignment */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_add(mb_interpreter_t* s, void** l) {
|
|
/* Operator + */
|
|
int result = MB_FUNC_OK;
|
|
|
|
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_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;
|
|
}
|
|
|
|
int _core_min(mb_interpreter_t* s, void** l) {
|
|
/* Operator - */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_instruct_num_op_num(-, l);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_mul(mb_interpreter_t* s, void** l) {
|
|
/* Operator * */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_instruct_num_op_num(*, l);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_div(mb_interpreter_t* s, void** l) {
|
|
/* Operator / */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_proc_div_by_zero(s, l, _exit, result, SE_RN_DIVIDE_BY_ZERO);
|
|
_instruct_num_op_num(/, l);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_mod(mb_interpreter_t* s, void** l) {
|
|
/* Operator MOD */
|
|
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;
|
|
}
|
|
|
|
int _core_pow(mb_interpreter_t* s, void** l) {
|
|
/* Operator ^ */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_instruct_fun_num_num(pow, l);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_open_bracket(mb_interpreter_t* s, void** l) {
|
|
/* Operator ( */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_close_bracket(mb_interpreter_t* s, void** l) {
|
|
/* Operator ) */
|
|
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;
|
|
}
|
|
|
|
int _core_neg(mb_interpreter_t* s, void** l) {
|
|
/* Operator - (negative) */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
_running_context_t* running = 0;
|
|
int* inep = 0;
|
|
int calc_depth = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
running = s->running_context;
|
|
|
|
if(!_ls_empty(s->in_neg_expr))
|
|
inep = (int*)_ls_back(s->in_neg_expr)->data;
|
|
|
|
if(inep)
|
|
(*inep)++;
|
|
|
|
calc_depth = running->calc_depth;
|
|
running->calc_depth = 1;
|
|
|
|
mb_make_nil(arg);
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
int _core_equal(mb_interpreter_t* s, void** l) {
|
|
/* Operator = (equal) */
|
|
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;
|
|
}
|
|
|
|
int _core_less(mb_interpreter_t* s, void** l) {
|
|
/* Operator < */
|
|
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;
|
|
}
|
|
|
|
int _core_greater(mb_interpreter_t* s, void** l) {
|
|
/* Operator > */
|
|
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;
|
|
}
|
|
|
|
int _core_less_equal(mb_interpreter_t* s, void** l) {
|
|
/* Operator <= */
|
|
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;
|
|
}
|
|
|
|
int _core_greater_equal(mb_interpreter_t* s, void** l) {
|
|
/* Operator >= */
|
|
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;
|
|
}
|
|
|
|
int _core_not_equal(mb_interpreter_t* s, void** l) {
|
|
/* Operator <> */
|
|
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;
|
|
}
|
|
|
|
int _core_and(mb_interpreter_t* s, void** l) {
|
|
/* Operator AND */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_instruct_bool_op_bool(&&, l);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_or(mb_interpreter_t* s, void** l) {
|
|
/* Operator OR */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
_instruct_bool_op_bool(||, l);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_not(mb_interpreter_t* s, void** l) {
|
|
/* Operator NOT */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
_running_context_t* running = 0;
|
|
int calc_depth = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
running = s->running_context;
|
|
|
|
calc_depth = running->calc_depth;
|
|
running->calc_depth = 1;
|
|
|
|
mb_make_nil(arg);
|
|
|
|
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:
|
|
arg.value.integer = true;
|
|
arg.type = MB_DT_INT;
|
|
|
|
break;
|
|
case MB_DT_INT:
|
|
arg.value.integer = (int_t)(!arg.value.integer);
|
|
|
|
break;
|
|
case MB_DT_REAL:
|
|
arg.value.integer = (int_t)(!((int_t)arg.value.float_point));
|
|
arg.type = MB_DT_INT;
|
|
|
|
break;
|
|
default:
|
|
arg.value.integer = false;
|
|
arg.type = MB_DT_INT;
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
running->calc_depth = calc_depth;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_is(mb_interpreter_t* s, void** l) {
|
|
/* Operator IS */
|
|
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);
|
|
|
|
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, 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 ||
|
|
_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_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_RN_NOT_SUPPORTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
#endif /* MB_ENABLE_CLASS */
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_let(mb_interpreter_t* s, void** l) {
|
|
/* LET statement */
|
|
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 int arr_idx = 0;
|
|
bool_t literally = false;
|
|
_object_t* val = 0;
|
|
#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, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
obj = (_object_t*)ast->data;
|
|
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) {
|
|
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, 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(obj->type != _DT_FUNC || strcmp(obj->data.func->name, "=") != 0) {
|
|
_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, TON(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
|
|
_proc_extra_var:
|
|
#endif /* MB_ENABLE_CLASS */
|
|
_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_RN_NOT_SUPPORTED, s->source_file, TON(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;
|
|
}
|
|
|
|
int _core_dim(mb_interpreter_t* s, void** l) {
|
|
/* DIM statement */
|
|
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_DIMENSION_TOO_MUCH, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
dummy.dimensions[dummy.dimension_count++] = (int)val.integer;
|
|
if(dummy.count)
|
|
dummy.count *= (unsigned int)val.integer;
|
|
else
|
|
dummy.count += (unsigned int)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;
|
|
}
|
|
|
|
int _core_if(mb_interpreter_t* s, void** l) {
|
|
/* IF statement */
|
|
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);
|
|
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);
|
|
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) {
|
|
ast = ast->prev;
|
|
} else {
|
|
if(multi_line) {
|
|
if(skip)
|
|
result = _skip_struct(s, &ast, _core_if, _core_endif);
|
|
}
|
|
}
|
|
|
|
*l = ast;
|
|
|
|
if(val->type != _DT_UNKNOWN)
|
|
_destroy_object(val, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_then(mb_interpreter_t* s, void** l) {
|
|
/* THEN statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_elseif(mb_interpreter_t* s, void** l) {
|
|
/* ELSEIF statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_else(mb_interpreter_t* s, void** l) {
|
|
/* ELSE statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_endif(mb_interpreter_t* s, void** l) {
|
|
/* ENDIF statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_for(mb_interpreter_t* s, void** l) {
|
|
/* FOR statement */
|
|
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;
|
|
}
|
|
|
|
int _core_in(mb_interpreter_t* s, void** l) {
|
|
/* IN statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_to(mb_interpreter_t* s, void** l) {
|
|
/* TO statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_step(mb_interpreter_t* s, void** l) {
|
|
/* STEP statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_next(mb_interpreter_t* s, void** l) {
|
|
/* NEXT statement */
|
|
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;
|
|
}
|
|
|
|
int _core_while(mb_interpreter_t* s, void** l) {
|
|
/* WHILE statement */
|
|
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);
|
|
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_FUNC_OK && result != MB_SUB_RETURN) { /* Normally */
|
|
goto _exit;
|
|
}
|
|
|
|
if(!ast) {
|
|
_handle_error_on_obj(s, SE_RN_SYNTAX, 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;
|
|
}
|
|
|
|
int _core_wend(mb_interpreter_t* s, void** l) {
|
|
/* WEND statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_do(mb_interpreter_t* s, void** l) {
|
|
/* DO statement */
|
|
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, 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);
|
|
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_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;
|
|
}
|
|
|
|
int _core_until(mb_interpreter_t* s, void** l) {
|
|
/* UNTIL statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_exit(mb_interpreter_t* s, void** l) {
|
|
/* EXIT statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
result = MB_LOOP_BREAK;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_goto(mb_interpreter_t* s, void** l) {
|
|
/* GOTO statement */
|
|
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;
|
|
}
|
|
|
|
int _core_gosub(mb_interpreter_t* s, void** l) {
|
|
/* GOSUB statement */
|
|
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;
|
|
}
|
|
|
|
int _core_return(mb_interpreter_t* s, void** l) {
|
|
/* RETURN statement */
|
|
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;
|
|
}
|
|
|
|
int _core_call(mb_interpreter_t* s, void** l) {
|
|
/* CALL statement */
|
|
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, 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));
|
|
}
|
|
|
|
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, 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:
|
|
routine = (_routine_t*)obj->data.routine;
|
|
result = _eval_routine(s, &ast, 0, 0, routine, _has_routine_lex_arg, _pop_routine_lex_arg);
|
|
if(ast)
|
|
ast = ast->prev;
|
|
|
|
break;
|
|
default: /* Do nothing */
|
|
break;
|
|
}
|
|
|
|
*l = ast;
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_def(mb_interpreter_t* s, void** l) {
|
|
/* DEF statement */
|
|
int result = MB_FUNC_OK;
|
|
_ls_node_t* ast = 0;
|
|
_running_context_t* running = 0;
|
|
_object_t* obj = 0;
|
|
_var_t* var = 0;
|
|
_ls_node_t* rnode = 0;
|
|
_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, 0, 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;
|
|
}
|
|
ast = ast->next;
|
|
routine->func.basic.entry = ast;
|
|
|
|
_skip_to(s, &ast, _core_enddef, _DT_INVALID);
|
|
|
|
ast = ast->next;
|
|
|
|
_exit:
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_enddef(mb_interpreter_t* s, void** l) {
|
|
/* ENDDEF statement */
|
|
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;
|
|
}
|
|
|
|
int _core_args(mb_interpreter_t* s, void** l) {
|
|
/* ... (variable argument list) statement */
|
|
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));
|
|
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
|
|
int _core_class(mb_interpreter_t* s, void** l) {
|
|
/* CLASS statement */
|
|
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, 0, 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);
|
|
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 */
|
|
_search_class_meta_function(s, instance, _CLASS_HASH_FUNC, &instance->hash);
|
|
_search_class_meta_function(s, instance, _CLASS_COMPARE_FUNC, &instance->compare);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
int _core_endclass(mb_interpreter_t* s, void** l) {
|
|
/* ENDCLASS statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
_do_nothing(s, l, _exit, result);
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _core_new(mb_interpreter_t* s, void** l) {
|
|
/* NEW statement */
|
|
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);
|
|
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);
|
|
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, TON(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;
|
|
}
|
|
|
|
int _core_var(mb_interpreter_t* s, void** l) {
|
|
/* VAR statement */
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
}
|
|
|
|
_exit:
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_reflect(mb_interpreter_t* s, void** l) {
|
|
/* REFLECT statement */
|
|
#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, TON(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
|
|
int _core_lambda(mb_interpreter_t* s, void** l) {
|
|
/* LAMBDA statement */
|
|
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 int 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)) {
|
|
void* v = 0;
|
|
_mb_check_mark(mb_get_var(s, l, &v), result, _error);
|
|
|
|
if(!routine->func.lambda.parameters)
|
|
routine->func.lambda.parameters = _ls_create();
|
|
if(!v || ((_object_t*)v)->type != _DT_VAR) {
|
|
_handle_error_on_obj(s, SE_RN_INVALID_LAMBDA, s->source_file, TON(l), MB_FUNC_ERR, _error, result);
|
|
}
|
|
var = ((_object_t*)v)->data.variable;
|
|
|
|
/* Add lambda parameters */
|
|
obj = 0;
|
|
var = _create_var(&obj, var->name, 0, true);
|
|
#ifdef MB_ENABLE_CLASS
|
|
var->pathing = 0;
|
|
#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;
|
|
ast = ast->next;
|
|
*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, TON(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
|
|
int _core_mem(mb_interpreter_t* s, void** l) {
|
|
/* MEM statement */
|
|
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 */
|
|
|
|
int _core_type(mb_interpreter_t* s, void** l) {
|
|
/* TYPE statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
int i = 0;
|
|
|
|
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));
|
|
|
|
if(arg.type == MB_DT_STRING) {
|
|
mb_data_e types[] = {
|
|
MB_DT_NIL,
|
|
MB_DT_UNKNOWN,
|
|
MB_DT_TYPE,
|
|
MB_DT_INT,
|
|
MB_DT_REAL,
|
|
MB_DT_NUM,
|
|
MB_DT_STRING,
|
|
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,
|
|
#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;
|
|
}
|
|
}
|
|
}
|
|
arg.value.type = arg.type;
|
|
arg.type = MB_DT_TYPE;
|
|
|
|
_found:
|
|
mb_check(mb_push_value(s, l, arg));
|
|
|
|
return result;
|
|
}
|
|
|
|
int _core_import(mb_interpreter_t* s, void** l) {
|
|
/* IMPORT statement */
|
|
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;
|
|
}
|
|
|
|
int _core_end(mb_interpreter_t* s, void** l) {
|
|
/* END statement */
|
|
int result = MB_FUNC_OK;
|
|
|
|
mb_assert(s && l);
|
|
|
|
result = MB_FUNC_END;
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Standard lib */
|
|
int _std_abs(mb_interpreter_t* s, void** l) {
|
|
/* Get the absolute value of a number */
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_push_value(s, l, arg));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_sgn(mb_interpreter_t* s, void** l) {
|
|
/* Get the sign of a number */
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_sqr(mb_interpreter_t* s, void** l) {
|
|
/* Get the square root of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_floor(mb_interpreter_t* s, void** l) {
|
|
/* Get the greatest integer not greater than a number */
|
|
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)(arg.value.integer);
|
|
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_ceil(mb_interpreter_t* s, void** l) {
|
|
/* Get the least integer not less than a number */
|
|
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)(arg.value.integer);
|
|
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_fix(mb_interpreter_t* s, void** l) {
|
|
/* Get the integer format of a number */
|
|
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)(arg.value.integer);
|
|
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_round(mb_interpreter_t* s, void** l) {
|
|
/* Get the rounded integer of a number */
|
|
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)(arg.value.integer);
|
|
|
|
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, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_int(s, l, arg.value.integer));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_srnd(mb_interpreter_t* s, void** l) {
|
|
/* Set a random seed */
|
|
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 int)seed);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _std_rnd(mb_interpreter_t* s, void** l) {
|
|
/* Get a random value among 0 ~ 1 or among given bounds */
|
|
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, TON(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;
|
|
}
|
|
|
|
int _std_sin(mb_interpreter_t* s, void** l) {
|
|
/* Get the sin value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_cos(mb_interpreter_t* s, void** l) {
|
|
/* Get the cos value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_tan(mb_interpreter_t* s, void** l) {
|
|
/* Get the tan value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_asin(mb_interpreter_t* s, void** l) {
|
|
/* Get the asin value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_acos(mb_interpreter_t* s, void** l) {
|
|
/* Get the acos value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_atan(mb_interpreter_t* s, void** l) {
|
|
/* Get the atan value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_exp(mb_interpreter_t* s, void** l) {
|
|
/* Get the exp value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_log(mb_interpreter_t* s, void** l) {
|
|
/* Get the log value of a number */
|
|
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;
|
|
}
|
|
|
|
int _std_asc(mb_interpreter_t* s, void** l) {
|
|
/* Get the ASCII code of a character */
|
|
int result = MB_FUNC_OK;
|
|
char* arg = 0;
|
|
int_t val = 0;
|
|
|
|
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] == '\0') {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
memcpy(&val, arg, strlen(arg));
|
|
mb_check(mb_push_int(s, l, val));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_chr(mb_interpreter_t* s, void** l) {
|
|
/* Get the character of an ASCII code */
|
|
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;
|
|
}
|
|
|
|
int _std_left(mb_interpreter_t* s, void** l) {
|
|
/* Get a number of characters from the left of a string */
|
|
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) {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
#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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
case -1:
|
|
_handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
#else /* MB_ENABLE_UNICODE */
|
|
sub = (char*)mb_malloc(count + 1);
|
|
memcpy(sub, arg, count);
|
|
sub[count] = '\0';
|
|
#endif /* MB_ENABLE_UNICODE */
|
|
mb_check(mb_push_string(s, l, sub));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_mid(mb_interpreter_t* s, void** l) {
|
|
/* Get a number of characters from a specific position of a string */
|
|
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)) {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
#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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
case -1:
|
|
_handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
#else /* MB_ENABLE_UNICODE */
|
|
sub = (char*)mb_malloc(count + 1);
|
|
memcpy(sub, arg + start, count);
|
|
sub[count] = '\0';
|
|
#endif /* MB_ENABLE_UNICODE */
|
|
mb_check(mb_push_string(s, l, sub));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_right(mb_interpreter_t* s, void** l) {
|
|
/* Get a number of characters from the right of a string */
|
|
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) {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
#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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
case -1:
|
|
_handle_error_on_obj(s, SE_RN_INDEX_OUT_OF_BOUND, s->source_file, TON(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] = '\0';
|
|
#endif /* MB_ENABLE_UNICODE */
|
|
mb_check(mb_push_string(s, l, sub));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_str(mb_interpreter_t* s, void** l) {
|
|
/* Get the string format of a number */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t arg;
|
|
char* chr = 0;
|
|
|
|
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));
|
|
|
|
chr = (char*)mb_malloc(32);
|
|
memset(chr, 0, 32);
|
|
if(arg.type == MB_DT_INT) {
|
|
sprintf(chr, MB_INT_FMT, arg.value.integer);
|
|
} else if(arg.type == MB_DT_REAL) {
|
|
sprintf(chr, MB_REAL_FMT, arg.value.float_point);
|
|
} else {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
mb_check(mb_push_string(s, l, chr));
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_val(mb_interpreter_t* s, void** l) {
|
|
/* Get the number format of a string, or get the value of a dictionary iterator */
|
|
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_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));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
switch(arg.type) {
|
|
case MB_DT_STRING:
|
|
ret.value.integer = (int_t)mb_strtol(arg.value.string, &conv_suc, 0);
|
|
if(*conv_suc == '\0') {
|
|
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 == '\0') {
|
|
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, TON(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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_len(mb_interpreter_t* s, void** l) {
|
|
/* Get the length of a string or an array, or element count of a collection or a variable argument list */
|
|
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_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));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
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_RN_NOT_SUPPORTED, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _std_get(mb_interpreter_t* s, void** l) {
|
|
/* GET statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t coi;
|
|
mb_value_t arg;
|
|
_object_t ocoi;
|
|
#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_assert(s && l);
|
|
|
|
mb_make_nil(coi);
|
|
mb_make_nil(arg);
|
|
mb_make_nil(ret);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &coi));
|
|
_MAKE_NIL(&ocoi);
|
|
switch(coi.type) {
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
case MB_DT_LIST:
|
|
_public_value_to_internal_object(&coi, &ocoi);
|
|
mb_check(mb_pop_int(s, l, &index));
|
|
if(!_at_list(ocoi.data.list, index, &ret)) {
|
|
_handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
break;
|
|
case MB_DT_DICT:
|
|
_public_value_to_internal_object(&coi, &ocoi);
|
|
mb_check(mb_pop_value(s, l, &arg));
|
|
if(!_find_dict(ocoi.data.dict, &arg, &ret)) {
|
|
_handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
break;
|
|
case MB_DT_LIST_IT:
|
|
_public_value_to_internal_object(&coi, &ocoi);
|
|
if(ocoi.data.list_it && !ocoi.data.list_it->list->range_begin && ocoi.data.list_it->curr.node && ocoi.data.list_it->curr.node->data) {
|
|
_internal_object_to_public_value((_object_t*)ocoi.data.list_it->curr.node->data, &ret);
|
|
} else if(ocoi.data.list_it && ocoi.data.list_it->list->range_begin) {
|
|
mb_make_int(ret, ocoi.data.list_it->curr.ranging);
|
|
} else {
|
|
_handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
break;
|
|
case MB_DT_DICT_IT:
|
|
_public_value_to_internal_object(&coi, &ocoi);
|
|
if(ocoi.data.dict_it && ocoi.data.dict_it->curr_node && ocoi.data.dict_it->curr_node->extra) {
|
|
_internal_object_to_public_value((_object_t*)ocoi.data.dict_it->curr_node->extra, &ret);
|
|
} else {
|
|
_handle_error_on_obj(s, SE_RN_INVALID_ITERATOR, s->source_file, TON(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(&coi, &ocoi);
|
|
mb_check(mb_pop_string(s, l, &field));
|
|
field = mb_strupr(field);
|
|
fnode = _search_identifier_in_class(s, ocoi.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_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
mb_check(mb_push_value(s, l, ret));
|
|
|
|
_exit:
|
|
_assign_public_value(&coi, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _std_print(mb_interpreter_t* s, void** l) {
|
|
/* PRINT statement */
|
|
int result = MB_FUNC_OK;
|
|
_ls_node_t* ast = 0;
|
|
_object_t* obj = 0;
|
|
_object_t val_obj;
|
|
_object_t* val_ptr = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
val_ptr = &val_obj;
|
|
_MAKE_NIL(val_ptr);
|
|
|
|
++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, s->source_file, DON(ast), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
obj = (_object_t*)ast->data;
|
|
do {
|
|
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, 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);
|
|
}
|
|
}
|
|
|
|
goto _print;
|
|
}
|
|
#endif /* MB_ENABLE_CLASS */
|
|
/* Fall through */
|
|
case _DT_TYPE: /* Fall through */
|
|
case _DT_NIL: /* Fall through */
|
|
case _DT_INT: /* Fall through */
|
|
case _DT_REAL: /* Fall through */
|
|
case _DT_STRING: /* Fall through */
|
|
case _DT_ARRAY: /* 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);
|
|
_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) {
|
|
_get_printer(s)(val_ptr->data.string ? val_ptr->data.string : MB_NULL_STRING);
|
|
if(!val_ptr->ref && val_ptr->data.string) {
|
|
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)
|
|
val_ptr->data.usertype_ref->fmt(s, val_ptr->data.usertype_ref->usertype, _get_printer(s));
|
|
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;
|
|
_ls_node_t* tsn = _search_identifier_in_class(s, val_ptr->data.instance, _CLASS_TO_STRING_FUNC, 0, 0);
|
|
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, 0, 0) == MB_FUNC_OK) {
|
|
val_ptr = &val_obj;
|
|
_MAKE_NIL(val_ptr);
|
|
_public_value_to_internal_object(&s->running_context->intermediate_value, val_ptr);
|
|
if(val_ptr->type == _DT_STRING) {
|
|
val_ptr->data.string = mb_strdup(val_ptr->data.string, strlen(val_ptr->data.string) + 1);
|
|
val_ptr->ref = false;
|
|
mb_make_nil(s->running_context->intermediate_value);
|
|
}
|
|
obj = val_ptr;
|
|
got_tostr = true;
|
|
|
|
goto _print;
|
|
}
|
|
}
|
|
|
|
_get_printer(s)(mb_get_type_string(_internal_type_to_public_type(val_ptr->type)));
|
|
#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_RN_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;
|
|
}
|
|
|
|
int _std_input(mb_interpreter_t* s, void** l) {
|
|
/* INPUT statement */
|
|
int result = MB_FUNC_OK;
|
|
_ls_node_t* ast = 0;
|
|
_object_t* obj = 0;
|
|
char line[256];
|
|
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 _MSC_VER
|
|
getch();
|
|
#else /* _MSC_VER */
|
|
_get_inputer(s)(line, sizeof(line));
|
|
#endif /* _MSC_VER */
|
|
|
|
goto _exit;
|
|
}
|
|
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 != '\0') {
|
|
obj->data.variable->data->type = _DT_REAL;
|
|
obj->data.variable->data->data.float_point = (real_t)mb_strtod(line, &conv_suc);
|
|
if(*conv_suc != '\0') {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
}
|
|
ast = ast->next;
|
|
} else if(obj->data.variable->data->type == _DT_STRING) {
|
|
if(obj->data.variable->data->data.string) {
|
|
safe_free(obj->data.variable->data->data.string);
|
|
}
|
|
obj->data.variable->data->data.string = (char*)mb_malloc(sizeof(line));
|
|
memset(obj->data.variable->data->data.string, 0, sizeof(line));
|
|
_get_inputer(s)(line, sizeof(line));
|
|
strcpy(obj->data.variable->data->data.string, line);
|
|
ast = ast->next;
|
|
} else {
|
|
result = MB_FUNC_ERR;
|
|
|
|
goto _exit;
|
|
}
|
|
|
|
_exit:
|
|
*l = ast;
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Collection lib */
|
|
#ifdef MB_ENABLE_COLLECTION_LIB
|
|
int _coll_list(mb_interpreter_t* s, void** l) {
|
|
/* LIST statement */
|
|
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;
|
|
}
|
|
|
|
int _coll_dict(mb_interpreter_t* s, void** l) {
|
|
/* DICT statement */
|
|
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;
|
|
}
|
|
|
|
int _coll_push(mb_interpreter_t* s, void** l) {
|
|
/* PUSH statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t lst;
|
|
mb_value_t arg;
|
|
_object_t olst;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(lst);
|
|
mb_make_nil(arg);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &lst));
|
|
if(lst.type != MB_DT_LIST) {
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_MAKE_NIL(&olst);
|
|
_public_value_to_internal_object(&lst, &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));
|
|
|
|
mb_check(mb_push_value(s, l, lst));
|
|
|
|
_exit:
|
|
_assign_public_value(&lst, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_pop(mb_interpreter_t* s, void** l) {
|
|
/* POP statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t lst;
|
|
mb_value_t val;
|
|
_object_t olst;
|
|
_object_t ocoll;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(lst);
|
|
mb_make_nil(val);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &lst));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
if(lst.type != MB_DT_LIST) {
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
_MAKE_NIL(&olst);
|
|
_public_value_to_internal_object(&lst, &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(&lst, 0);
|
|
} else {
|
|
mb_check(mb_push_value(s, l, val));
|
|
|
|
_assign_public_value(&lst, 0);
|
|
|
|
_handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _coll_peek(mb_interpreter_t* s, void** l) {
|
|
/* PEEK statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t lst;
|
|
mb_value_t val;
|
|
_object_t olst;
|
|
_object_t* oval = 0;
|
|
_ls_node_t* node = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(lst);
|
|
mb_make_nil(val);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &lst));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
if(lst.type != MB_DT_LIST) {
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
_MAKE_NIL(&olst);
|
|
_public_value_to_internal_object(&lst, &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(&lst, 0);
|
|
} else {
|
|
mb_check(mb_push_value(s, l, val));
|
|
|
|
_assign_public_value(&lst, 0);
|
|
|
|
_handle_error_on_obj(s, SE_RN_EMPTY_COLLECTION, s->source_file, TON(l), MB_FUNC_WARNING, _exit, result);
|
|
}
|
|
|
|
_exit:
|
|
return result;
|
|
}
|
|
|
|
int _coll_insert(mb_interpreter_t* s, void** l) {
|
|
/* INSERT statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t lst;
|
|
int_t idx = 0;
|
|
mb_value_t arg;
|
|
_object_t olst;
|
|
_object_t* oval = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(lst);
|
|
mb_make_nil(arg);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &lst));
|
|
mb_check(mb_pop_int(s, l, &idx));
|
|
mb_check(mb_pop_value(s, l, &arg));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
if(lst.type != MB_DT_LIST) {
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_MAKE_NIL(&olst);
|
|
_public_value_to_internal_object(&lst, &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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
|
|
mb_check(mb_push_value(s, l, lst));
|
|
|
|
_exit:
|
|
_assign_public_value(&lst, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_sort(mb_interpreter_t* s, void** l) {
|
|
/* SORT statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t lst;
|
|
_object_t olst;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(lst);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &lst));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
if(lst.type != MB_DT_LIST) {
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
_MAKE_NIL(&olst);
|
|
_public_value_to_internal_object(&lst, &olst);
|
|
|
|
_sort_list(olst.data.list);
|
|
|
|
mb_check(mb_push_value(s, l, lst));
|
|
|
|
_exit:
|
|
_assign_public_value(&lst, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_exist(mb_interpreter_t* s, void** l){
|
|
/* EXIST statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t coll;
|
|
mb_value_t arg;
|
|
_object_t ocoll;
|
|
mb_value_t ret;
|
|
|
|
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));
|
|
mb_check(mb_pop_value(s, l, &arg));
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
ret.type = MB_DT_INT;
|
|
_MAKE_NIL(&ocoll);
|
|
switch(coll.type) {
|
|
case MB_DT_LIST:
|
|
_public_value_to_internal_object(&coll, &ocoll);
|
|
ret.value.integer = !!_find_list(ocoll.data.list, &arg);
|
|
|
|
break;
|
|
case MB_DT_DICT:
|
|
_public_value_to_internal_object(&coll, &ocoll);
|
|
ret.value.integer = !!_find_dict(ocoll.data.dict, &arg, 0);
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
mb_check(mb_push_value(s, l, ret));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_set(mb_interpreter_t* s, void** l) {
|
|
/* SET statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t coll;
|
|
int_t idx = 0;
|
|
mb_value_t key;
|
|
mb_value_t val;
|
|
_object_t ocoll;
|
|
_object_t* oval = 0;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(coll);
|
|
mb_make_nil(key);
|
|
mb_make_nil(val);
|
|
|
|
mb_check(mb_attempt_open_bracket(s, l));
|
|
|
|
mb_check(mb_pop_value(s, l, &coll));
|
|
_MAKE_NIL(&ocoll);
|
|
switch(coll.type) {
|
|
case MB_DT_LIST:
|
|
_public_value_to_internal_object(&coll, &ocoll);
|
|
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(ocoll.data.list, idx, &val, &oval)) {
|
|
_destroy_object(oval, 0);
|
|
|
|
_handle_error_on_obj(s, SE_RN_CANNOT_FIND_WITH_GIVEN_INDEX, s->source_file, TON(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_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(ocoll.data.dict, &key, &val, 0, 0);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
mb_check(mb_push_value(s, l, coll));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_remove(mb_interpreter_t* s, void** l) {
|
|
/* REMOVE statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t coll;
|
|
int_t idx = 0;
|
|
mb_value_t key;
|
|
_object_t ocoll;
|
|
|
|
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));
|
|
_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, TON(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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_COLLECTION_EXPECTED, s->source_file, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_attempt_close_bracket(s, l));
|
|
|
|
mb_check(mb_push_value(s, l, coll));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_clear(mb_interpreter_t* s, void** l) {
|
|
/* CLEAR statement */
|
|
int result = MB_FUNC_OK;
|
|
mb_value_t coll;
|
|
_object_t ocoll;
|
|
|
|
mb_assert(s && l);
|
|
|
|
mb_make_nil(coll);
|
|
|
|
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);
|
|
_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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_push_value(s, l, coll));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_clone(mb_interpreter_t* s, void** l) {
|
|
/* CLONE statement */
|
|
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);
|
|
switch(coll.type) {
|
|
case MB_DT_LIST:
|
|
_public_value_to_internal_object(&coll, &ocoll);
|
|
_clone_object(s, &ocoll, &otgt);
|
|
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);
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_push_value(s, l, ret));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_iterator(mb_interpreter_t* s, void** l) {
|
|
/* ITERATOR statement */
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
|
|
break;
|
|
}
|
|
|
|
mb_check(mb_push_value(s, l, ret));
|
|
|
|
_exit:
|
|
_assign_public_value(&coll, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
int _coll_move_next(mb_interpreter_t* s, void** l) {
|
|
/* MOVE_NEXT statement */
|
|
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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
} else if(oit.data.list_it) {
|
|
ret.type = MB_DT_INT;
|
|
ret.value.integer = 1;
|
|
} 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, TON(l), MB_FUNC_ERR, _exit, result);
|
|
} else if(oit.data.dict_it) {
|
|
ret.type = MB_DT_INT;
|
|
ret.value.integer = 1;
|
|
} else {
|
|
mb_make_nil(ret);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
_handle_error_on_obj(s, SE_RN_ITERATOR_EXPECTED, s->source_file, TON(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 __BORLANDC__
|
|
# pragma warn .8004
|
|
# pragma warn .8008
|
|
# pragma warn .8012
|
|
#endif /* __BORLANDC__ */
|
|
|
|
#ifdef __APPLE__
|
|
# pragma clang diagnostic pop
|
|
#endif /* __APPLE__ */
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning(pop)
|
|
#endif /* _MSC_VER */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|