-
Notifications
You must be signed in to change notification settings - Fork 122
Inline data sequence module
This module implements an inline data sequence library with MY-BASIC.
It was widly used to transfer inline data with the DATA
, READ
, RESTORE
statements, but it's not common to see nowadays. Consider using better data management over this, however, it's still possible to implement it in some way.
I write this inline data sequence module for a purpose of habit compatibility. 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 location restoration: DATA
returns total collected count until the last data argument; READ
returns latest reading location; RESTORE
accepts an indicated target location, 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 bool_t _is_ref(mb_value_t val); /* Check whether a value is referenced */
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) {
unsigned i = 0;
for(i = 0; i < sequ->count; ++i) {
mb_value_t &val = sequ->value[i];
if(_is_ref(val))
mb_unref_value(s, nullptr, val);
}
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));
if(_is_ref(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->value, sizeof(mb_value_t) * _data_seq->size);
for(i = _data_seq->count; i < _data_seq->size; ++i) {
mb_make_nil(_data_seq->value[i]);
}
}
if(_is_ref(_data_seq->value[_data_seq->count]))
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, true));
if(v == 0) {
result = MB_FUNC_ERR;
break;
}
if(_data_seq->cursor >= _data_seq->count) {
result = MB_FUNC_ERR;
break;
}
if(_is_ref(_data_seq->value[_data_seq->cursor]))
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);
Referenced values may become unreachable by GC after they are obtained by the DATA
statement, nevertheless they should be marked as alive. Consider using mb_set_alive_checker
to tell the collector the aliveness of values.
The _alloc_data_seq
function should be called 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();
BASIC 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