diff --git a/HISTORY b/HISTORY index 9a04127..0618bc5 100755 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,7 @@ +Dec. 4 2015 +Updated icon +Added structures for class development + Nov. 30 2015 Improved stability for sub routine Improved error handling with shell diff --git a/README.md b/README.md index 4d247c9..db1bbf8 100755 --- a/README.md +++ b/README.md @@ -2,17 +2,21 @@ **Copyright (C) 2011 - 2015 [Wang Renxin](https://cn.linkedin.com/pub/wang-renxin/43/494/20). All rights reserved.** -Why are existing script interpreters so complex? Why is it so difficult to integrate with them? Why not try MY-BASIC today! Scripting should be simple and enjoyable. +Why are existing script interpreters so complex? Why is it so difficult to integrate with them? Why not try MY-BASIC today! Script should be simple and enjoyable. ## Build status [![Build Status](https://travis-ci.org/paladin-t/my_basic.svg?branch=master)](https://travis-ci.org/paladin-t/my_basic) +## Development status + +* Class support under development + ## Contents * [Introduction](#introduction) * [Main features](#main-features) -* [Scripting at a glance](#scripting-at-a-glance) +* [Script at a glance](#script-at-a-glance) * [Interpreter workflow diagram](#interpreter-workflow-diagram) * [Installation](#installation) * [Wiki](#wiki) @@ -20,22 +24,22 @@ Why are existing script interpreters so complex? Why is it so difficult to integ ## Introduction -MY-BASIC is a lightweight cross-platform easy extendable BASIC interpreter written in pure C with about 10,000 lines of source code. Its grammar is similar to structured BASIC. It is aimed to be either an embeddable scripting language or a standalone interpreter. The core is pretty light; all in a C source file and an associated header file. You are able to easily combine MY-BASIC with an existing project in C, C++, Objective-C, etc. Scripting driven can make your projects more powerful, elegant and neat. +MY-BASIC is a lightweight cross-platform easy extendable BASIC interpreter written in pure C with about 10,000 lines of source code. Its grammar is similar to structured BASIC. It is aimed to be either an embeddable scripting language or a standalone interpreter. The core is pretty light; all in a C source file and an associated header file. It's able to easily combine MY-BASIC with an existing project in C, C++, Objective-C, etc. Script driven can make your projects more powerful, elegant and neat. ## Main features * It is totally **free** to use MY-BASIC for individual or commercial purpose under the MIT license -* Written in clean **ANSI C**, source code portable +* Written in clean **ANSI C**, source code is portable for a dozen of platforms * With most common BASIC syntax * **Lightweight** (within memory usage less than 128KB) and fast * Case-insensitive tokenization * Integer, float point, string, boolean, user defined data types, etc. with array support +* Standard numeric functions, and standard string functions * Referenced usertype support * Collection implementation and manipulation functions for **`LIST`** and **`DICT`** -* Automatic releasing of referenced objects (list, dictionary, referenced usertype, etc.) benefited from **Reference Counting** and **GC** -* Standard numeric functions, and standard string functions +* Automatic releasing of referenced objects (list, dictionary, referenced usertype, etc.) benefited from **Reference Counting** and **Garbage Collection** * Multiple file support by `IMPORT` statement -* Structured user costomized **sub** routine definition by **`DEF/ENDDEF`** support, including tail recursion optimization +* Structured user customizable **sub** routine definition by **`DEF/ENDDEF`** support, including tail recursion optimization * Structured `IF-THEN-ELSEIF-ELSE-ENDIF` support * Structured `FOR-TO-STEP-NEXT/WHILE-WEND/DO-UNTIL` support * `GOTO/GOSUB-RETURN` support @@ -44,10 +48,10 @@ MY-BASIC is a lightweight cross-platform easy extendable BASIC interpreter writt * High expansibility, easy to use APIs, easy to write customized scripting interfaces * It's able to use it as a standalone interpreter, or integrate it with existing projects in C, C++, Objective-C, etc. * It's able to learn how to build an interpreter from scratch with MY-BASIC -* It's able to build your own dialect based on MY-BASIC +* It's able to build your own dialect easily based on MY-BASIC * More features/modules under developing -## Scripting at a glance +## Script at a glance Come along with a traditional "hello world" script in MY-BASIC: @@ -75,20 +79,20 @@ Read the [MY-BASIC Quick Reference](MY-BASIC%20Quick%20Reference.pdf) (especiall This repository contains precompiled binaries for [Windows](output/my_basic.exe) and [OS X](output/my_basic_mac), it's efficient to download it and have a first impressive playground. Or you could make a build as follow. * Open the Visual Studio solution `my_basic.sln` on Windows to build an executable -* Open the XCode solution `my_basic_mac.xcodeproj` on OS X to build an OS X executable -* Use the `makefile` with "make" toolchain to build an interpreter binary according to your platform +* Open the Xcode solution `my_basic_mac.xcodeproj` on OS X to build an OS X executable +* If you are not using VS or Xcode, use the `makefile` with a "make" toolchain to build an interpreter binary according to your own platform To compile an interpreter binary for your own platform manually, please follow the steps. -* Retrieve at least `core` and `shell` folders for minimum build -* Setup your compile toolchain configuration -* Use your compiler to compile `core/my_basic.c` and `shell/main.c`, including `core/my_basic.h` is required for both source files, then link up your own executable +1. Retrieve at least `core` and `shell` folders for minimum build +2. Setup your compile toolchain configuration +3. Use your compiler to compile `core/my_basic.c` and `shell/main.c`, including `core/my_basic.h` is required for both source files, then link up your own executable The standalone interpreter supports three modes. -* Execute the binary directly without arguments to interactive with MY-BASIC +* Execute the binary directly without arguments to enter MY-BASIC interactive mode * Pass a file path to the binary to load and run that script file -* Pass an argument `-e` and an expression to evaluate and print it, eg. `-e "2 * (3 + 4)"` +* Pass an argument `-e` followed by an expression to evaluate and print it, eg. `-e "2 * (3 + 4)"` ### Combine with exist projects @@ -96,10 +100,12 @@ MY-BASIC is cleanly written in a single C source file and an associated header f You can definitely [link with MY-BASIC as a lib](https://github.com/paladin-t/my_basic/wiki/Link-with-MY_BASIC) as well. -For more details about using MY-BASIC when it's already integrated with exist projects, please see [MY-BASIC Quick Reference](MY-BASIC%20Quick%20Reference.pdf). +For more details about using MY-BASIC when it's already integrated with a project, please see [MY-BASIC Quick Reference](MY-BASIC%20Quick%20Reference.pdf). ## [Wiki](https://github.com/paladin-t/my_basic/wiki) +Most of the fundamental topics are mentioned in the [MY-BASIC Quick Reference](MY-BASIC%20Quick%20Reference.pdf). There are some other scattered topics besides these aspects, such as the desine principle, machinism behind MY-BASIC, effective practice, etc; they are issued in the [Wiki](https://github.com/paladin-t/my_basic/wiki). + * Principles * [Passes](https://github.com/paladin-t/my_basic/wiki/Passes) * [Interpreter workflow diagram](https://github.com/paladin-t/my_basic/wiki/Interpreter-workflow-diagram) diff --git a/core/my_basic.c b/core/my_basic.c index 28eff57..e71ed84 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -81,7 +81,7 @@ extern "C" { /** Macros */ #define _VER_MAJOR 1 #define _VER_MINOR 1 -#define _VER_REVISION 101 +#define _VER_REVISION 102 #define _VER_SUFFIX #define _MB_VERSION ((_VER_MAJOR * 0x01000000) + (_VER_MINOR * 0x00010000) + (_VER_REVISION)) #define _STRINGIZE(A) _MAKE_STRINGIZE(A) @@ -229,6 +229,7 @@ static const char* _ERR_DESC[] = { "Invalid routine", "Routine expected", "Duplicate routine", + "Invalid class", "Collection expected", "Iterator expected", "Collection or iterator expected", @@ -261,8 +262,10 @@ typedef enum _data_e { _DT_DICT_IT, #endif /* MB_ENABLE_COLLECTION_LIB */ _DT_LABEL, /* Label type, used for GOTO, GOSUB statement */ - _DT_ROUTINE, /* User defined sub routine in script */ +#ifdef MB_ENABLE_CLASS _DT_CLASS, /* Object instance */ +#endif /* MB_ENABLE_CLASS */ + _DT_ROUTINE, /* User defined sub routine in script */ _DT_SEP, /* Separator */ _DT_EOS /* End of statement */ } _data_e; @@ -357,14 +360,18 @@ typedef struct _label_t { _ls_node_t* node; } _label_t; +#ifdef MB_ENABLE_CLASS typedef struct _class_t { char* name; struct _running_context_t* scope; } _class_t; +#endif /* MB_ENABLE_CLASS */ typedef struct _routine_t { char* name; +#ifdef MB_ENABLE_CLASS _class_t* instance; +#endif /* MB_ENABLE_CLASS */ struct _running_context_t* scope; _ls_node_t* entry; _ls_node_t* parameters; @@ -396,8 +403,10 @@ typedef struct _object_t { _dict_it_t* dict_it; #endif /* MB_ENABLE_COLLECTION_LIB */ _label_t* label; - _routine_t* routine; +#ifdef MB_ENABLE_CLASS _class_t* instance; +#endif /* MB_ENABLE_CLASS */ + _routine_t* routine; char separator; mb_val_bytes_t bytes; _raw_t raw; @@ -440,7 +449,9 @@ MBAPI const size_t MB_SIZEOF_ARR = _MB_MEM_TAG_SIZE + sizeof(_array_t); MBAPI const size_t MB_SIZEOF_VAR = _MB_MEM_TAG_SIZE + sizeof(_var_t); MBAPI const size_t MB_SIZEOF_LBL = _MB_MEM_TAG_SIZE + sizeof(_label_t); 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); @@ -453,7 +464,9 @@ MBAPI const size_t MB_SIZEOF_ARR = sizeof(_array_t); MBAPI const size_t MB_SIZEOF_VAR = sizeof(_var_t); MBAPI const size_t MB_SIZEOF_LBL = sizeof(_label_t); 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 @@ -1150,9 +1163,11 @@ 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_CLASS static void _init_class(mb_interpreter_t* s, _class_t* instance, char* n); static void _begin_class(mb_interpreter_t* s); -static void _end_class(mb_interpreter_t* s); +static bool_t _end_class(mb_interpreter_t* s); +#endif /* MB_ENABLE_CLASS */ static void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n); static void _begin_routine(mb_interpreter_t* s); static bool_t _end_routine(mb_interpreter_t* s); @@ -1415,8 +1430,10 @@ static const _func_t _core_libs[] = { { "CALL", _core_call }, { "DEF", _core_def }, { "ENDDEF", _core_enddef }, +#ifdef MB_ENABLE_CLASS { "CLASS", _core_class }, { "ENDCLASS", _core_endclass }, +#endif /* MB_ENABLE_CLASS */ #ifdef MB_ENABLE_ALLOC_STAT { "MEM", _core_mem }, @@ -3211,7 +3228,13 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob /* Create a syntax symbol */ int result = MB_FUNC_OK; _data_e type; - union { _func_t* func; _array_t* array; _class_t* instance; _routine_t* routine; _var_t* var; _label_t* label; real_t float_point; int_t integer; _raw_t any; } tmp; + 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; @@ -3304,6 +3327,7 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob } break; +#ifdef MB_ENABLE_CLASS case _DT_CLASS: glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym); if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_CLASS) { @@ -3332,6 +3356,7 @@ int _create_symbol(mb_interpreter_t* s, _ls_node_t* l, char* sym, _object_t** ob } break; +#endif /* MB_ENABLE_CLASS */ case _DT_ROUTINE: glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym); if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_ROUTINE) { @@ -3563,6 +3588,7 @@ _end_import: goto _exit; } /* _class_t */ +#ifdef MB_ENABLE_CLASS if(context->last_symbol) { glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym); if(glbsyminscope && ((_object_t*)glbsyminscope->data)->type == _DT_CLASS) { @@ -3581,26 +3607,28 @@ _end_import: goto _exit; } if(glbsyminscope && ((_object_t*)(glbsyminscope->data))->type == _DT_VAR) { - tmp.obj = (_object_t*)(glbsyminscope->data); - if(!tmp.obj->ref) { - _ht_remove(running->var_dict, sym, _ls_cmp_extra_string); - _dispose_object(tmp.obj); + _handle_error_now(s, SE_RN_INVALID_CLASS, 0, 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_CLASS, 0, MB_FUNC_ERR); + + goto _exit; } - tmp.obj->type = _DT_CLASS; - tmp.obj->data.instance = (_class_t*)mb_malloc(sizeof(_class_t)); - _init_class(s, tmp.obj->data.instance, sym); - _init_class(s, tmp.obj->data.instance, sym); - _ht_set_or_insert(running->var_dict, sym, tmp.obj); } result = _DT_CLASS; goto _exit; } else if(_IS_FUNC(context->last_symbol, _core_endclass)) { - _end_class(s); - _pop_scope(s); + if(_end_class(s)) + _pop_scope(s); } } +#endif /* MB_ENABLE_CLASS */ /* _routine_t */ if(context->last_symbol) { glbsyminscope = _search_identifier_in_scope_chain(s, 0, sym); @@ -5080,6 +5108,7 @@ void _destroy_ref(_ref_t* ref) { ref->on_unref = 0; } +#ifdef MB_ENABLE_CLASS void _init_class(mb_interpreter_t* s, _class_t* instance, char* n) { /* Initialize a class */ _running_context_t* running = 0; @@ -5105,15 +5134,23 @@ void _begin_class(mb_interpreter_t* s) { context->class_state++; } -void _end_class(mb_interpreter_t* s) { +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->class_state) { + _handle_error_now(s, SE_RN_INVALID_CLASS, 0, MB_FUNC_ERR); + + return false; + } context->class_state--; + + return true; } +#endif /* MB_ENABLE_CLASS */ void _init_routine(mb_interpreter_t* s, _routine_t* routine, char* n) { /* Initialize a routine */ @@ -5454,10 +5491,12 @@ int _clone_object(_object_t* obj, _object_t* tgt) { tgt->data.label->node = obj->data.label->node; break; +#ifdef MB_ENABLE_CLASS case _DT_CLASS: mb_assert(0 && "Not implemented"); break; +#endif /* MB_ENABLE_CLASS */ case _DT_ROUTINE: mb_assert(0 && "Not implemented"); @@ -5545,6 +5584,7 @@ int _dispose_object(_object_t* obj) { } break; +#ifdef MB_ENABLE_CLASS case _DT_CLASS: if(!obj->ref) { safe_free(obj->data.instance->name); @@ -5555,6 +5595,7 @@ int _dispose_object(_object_t* obj) { } break; +#endif /* MB_ENABLE_CLASS */ case _DT_ROUTINE: if(!obj->ref) { safe_free(obj->data.routine->name); @@ -5796,6 +5837,10 @@ _data_e _public_type_to_internal_type(mb_data_e t) { 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: @@ -5832,6 +5877,10 @@ mb_data_e _internal_type_to_public_type(_data_e t) { 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: @@ -5911,6 +5960,11 @@ int _public_value_to_internal_object(mb_value_t* pbl, _object_t* itn) { 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; +#endif /* MB_ENABLE_CLASS */ case MB_DT_ROUTINE: itn->type = _DT_ROUTINE; itn->data.routine = (_routine_t*)pbl->value.routine; @@ -5993,6 +6047,13 @@ int _internal_object_to_public_value(_object_t* itn, mb_value_t* pbl) { 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; @@ -6187,10 +6248,12 @@ int _execute_statement(mb_interpreter_t* s, _ls_node_t** l) { _handle_error_on_obj(s, SE_RN_INVALID_EXPRESSION, 0, DON(ast), MB_FUNC_ERR, _exit, result); break; +#ifdef MB_ENABLE_CLASS case _DT_CLASS: mb_assert(0 && "Not implemented"); break; +#endif /* MB_ENABLE_CLASS */ case _DT_ROUTINE: ast = ast->prev; result = _core_call(s, (void**)(&ast)); @@ -8175,6 +8238,12 @@ const char* mb_get_type_string(mb_data_e t) { case MB_DT_DICT_IT: return "DICT_ITERATOR"; #endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + case MB_DT_CLASS: + return "CLASS"; +#endif /* MB_ENABLE_CLASS */ + case MB_DT_ROUTINE: + return "ROUTINE"; default: return "UNKNOWN"; } diff --git a/core/my_basic.h b/core/my_basic.h index f4c1a32..2af4b7a 100755 --- a/core/my_basic.h +++ b/core/my_basic.h @@ -81,6 +81,10 @@ extern "C" { # define MB_ENABLE_MODULE #endif /* MB_ENABLE_MODULE */ +#ifndef MB_ENABLE_CLASS +# define MB_ENABLE_CLASS +#endif /* MB_ENABLE_CLASS */ + #ifndef MB_COMPACT_MODE # define MB_COMPACT_MODE #endif /* MB_COMPACT_MODE */ @@ -309,6 +313,7 @@ typedef enum mb_error_e { SE_RN_INVALID_ROUTINE, SE_RN_ROUTINE_EXPECTED, SE_RN_DUPLICATE_ROUTINE, + SE_RN_INVALID_CLASS, SE_RN_COLLECTION_EXPECTED, SE_RN_ITERATOR_EXPECTED, SE_RN_COLLECTION_OR_ITERATOR_EXPECTED, @@ -337,7 +342,10 @@ typedef enum mb_data_e { MB_DT_DICT = 1 << 15, MB_DT_DICT_IT = 1 << 16, #endif /* MB_ENABLE_COLLECTION_LIB */ - MB_DT_ROUTINE = 1 << 24 +#ifdef MB_ENABLE_CLASS + MB_DT_CLASS = 1 << 24, +#endif /* MB_ENABLE_CLASS */ + MB_DT_ROUTINE = 1 << 25 } mb_data_e; typedef unsigned char mb_val_bytes_t[sizeof(void*)]; @@ -356,6 +364,9 @@ typedef union mb_value_u { void* dict; void* dict_it; #endif /* MB_ENABLE_COLLECTION_LIB */ +#ifdef MB_ENABLE_CLASS + void* instance; +#endif /* MB_ENABLE_CLASS */ void* routine; mb_val_bytes_t bytes; } mb_value_u; diff --git a/output/my_basic.exe b/output/my_basic.exe index 52ad1dd..2099e22 100755 Binary files a/output/my_basic.exe and b/output/my_basic.exe differ diff --git a/shell/main.c b/shell/main.c index 9d60afc..b04de1d 100755 --- a/shell/main.c +++ b/shell/main.c @@ -125,7 +125,11 @@ extern MBAPI const size_t MB_SIZEOF_ARR; extern MBAPI const size_t MB_SIZEOF_VAR; extern MBAPI const size_t MB_SIZEOF_LBL; extern MBAPI const size_t MB_SIZEOF_RTN; +#ifdef MB_ENABLE_CLASS extern MBAPI const size_t MB_SIZEOF_CLS; +#else /* MB_ENABLE_CLASS */ +static const size_t MB_SIZEOF_CLS = 20; +#endif /* MB_ENABLE_CLASS */ typedef unsigned _pool_chunk_size_t;