-
Notifications
You must be signed in to change notification settings - Fork 126
Inline data sequence module
This is a demonstration about how to implement an inline data sequence module with MY-BASIC.
Using of the classical DATA
, READ
, RESTORE
statements is an oversimplified and crude way for data enumeration, it fits more appropriately to it by using dedicated data file. But they works very well since BASIC was introduced, and even now it does make sense for short and quick code.
I write this inline data sequence module for a habit compatibility purpose. But there're some differences. Retro BASIC collects data before the program runs, and only accepts simple data; but with this module, MY-BASIC really executes a DATA
statement at runtime and it must appear before READ
, it can also collect advanced data types in MY-BASIC. The DATA
, READ
, RESTORE
statements act some different behaviour for cursor restoration: DATA
returns total collected count until the last data argument; READ
returns latest reading cursor; RESTORE
accepts an indicated target position, or zero as default. See the code for details.
Add implementation:
typedef struct _data_seq_t {
mb_value_t* value;
unsigned count;
unsigned size;
unsigned cursor;
} _data_seq_t;
static _data_seq_t* _data_seq = 0;
#define _DATA_SEQ_INC_STEP 32
static void _alloc_data_seq(void) {
unsigned i = 0;
_data_seq = (_data_seq_t*)malloc(sizeof(_data_seq_t));
_data_seq->count = 0;
_data_seq->size = _DATA_SEQ_INC_STEP;
_data_seq->cursor = 0;
_data_seq->value = (mb_value_t*)malloc(sizeof(mb_value_t) * _data_seq->size);
for(i = 0; i < _data_seq->size; ++i) {
mb_make_nil(_data_seq->value[i]);
}
}
static void _free_data_seq(void) {
free(_data_seq->value);
free(_data_seq);
_data_seq = 0;
}
static int _data(struct mb_interpreter_t* s, void** l) {
int result = MB_FUNC_OK;
mb_value_t tmp;
mb_assert(s && l);
mb_check(mb_attempt_func_begin(s, l));
while(mb_has_arg(s, l)) {
mb_check(mb_pop_value(s, l, &tmp));
mb_ref_value(s, l, tmp);
if(_data_seq->count + 1 == _data_seq->size) {
unsigned i = 0;
_data_seq->size += _DATA_SEQ_INC_STEP;
_data_seq->value = (mb_value_t*)realloc(_data_seq, sizeof(mb_value_t) * _data_seq->size);
for(i = _data_seq->count; i < _data_seq->size; ++i) {
mb_make_nil(_data_seq->value[i]);
}
}
mb_unref_value(s, l, _data_seq->value[_data_seq->count]);
_data_seq->value[_data_seq->count++] = tmp;
}
mb_check(mb_attempt_func_end(s, l));
mb_check(mb_push_int(s, l, (int_t)_data_seq->count));
return result;
}
static int _read(struct mb_interpreter_t* s, void** l) {
int result = MB_FUNC_OK;
mb_assert(s && l);
mb_check(mb_attempt_func_begin(s, l));
while(mb_has_arg(s, l)) {
void* v = 0;
mb_check(mb_get_var(s, l, &v));
if(v == 0) {
result = MB_FUNC_ERR;
break;
}
if(_data_seq->cursor >= _data_seq->count) {
result = MB_FUNC_ERR;
break;
}
mb_ref_value(s, l, _data_seq->value[_data_seq->cursor]);
mb_set_var_value(s, v, _data_seq->value[_data_seq->cursor++]);
}
mb_check(mb_attempt_func_end(s, l));
mb_check(mb_push_int(s, l, (int_t)_data_seq->cursor));
return result;
}
static int _restore(struct mb_interpreter_t* s, void** l) {
int result = MB_FUNC_OK;
unsigned cur = 0;
mb_assert(s && l);
mb_check(mb_attempt_func_begin(s, l));
if(mb_has_arg(s, l)) {
int_t val = 0;
mb_check(mb_pop_int(s, l, &val));
cur = (unsigned)val;
}
mb_check(mb_attempt_func_end(s, l));
if(cur > _data_seq->count)
result = MB_FUNC_ERR;
else
_data_seq->cursor = cur;
return result;
}
Register them:
mb_register_func(bas, "DATA", _data);
mb_register_func(bas, "READ", _read);
mb_register_func(bas, "RESTORE", _restore);
You have to call _alloc_data_seq
before mb_run
and _free_data_seq
after it, to allocate and free necessary structure:
_alloc_data_seq();
mb_run(bas, true);
_free_data_seq();
Script usage:
data 22, 7, 355, 113, "hello", list(1 to 42)
pos = read a, b
read c, d
print a / b; c / d;
restore pos
read w, x, y, z
print w; x; y; len(z);
- Principles
- Coding
- Data types
- Standalone shell
- Integration
- Customization
- More scripting API
- FAQ