Skip to content

Inline data sequence module

Wang Renxin edited this page Dec 25, 2016 · 8 revisions

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 is run, 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);

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