Skip to content

Inline data sequence module

Wang Renxin edited this page May 31, 2022 · 8 revisions

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);
Clone this wiki locally