+added structures for class development; *updated readme doc; *updated exe icon.

This commit is contained in:
paladin-t 2015-12-04 11:26:42 +08:00
parent bf9f5ec86c
commit f6e9f9fbb1
6 changed files with 129 additions and 35 deletions

View File

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

View File

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

View File

@ -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);
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";
}

View File

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

Binary file not shown.

View File

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