예제 #1
0
파일: f-extension.c 프로젝트: MannyZhong/r3
*/	void Do_Command(REBVAL *value)
/*
**	Evaluates the arguments for a command function and creates
**	a resulting stack frame (struct or object) for command processing.
**
**	A command value consists of:
**		args - same as other funcs
**		spec - same as other funcs
**		body - [ext-obj func-index]
**
***********************************************************************/
{
	REBVAL *val = BLK_HEAD(VAL_FUNC_BODY(value));
	REBEXT *ext;
	REBCNT cmd;
	REBCNT argc;
	REBCNT n;
	RXIFRM frm;	// args stored here

	// All of these were checked above on definition:
	val = BLK_HEAD(VAL_FUNC_BODY(value));
	cmd = (int)VAL_INT64(val+1);
	ext = &Ext_List[VAL_I32(VAL_OBJ_VALUE(val, 1))]; // Handler

	// Copy args to command frame (array of args):
	RXA_COUNT(&frm) = argc = SERIES_TAIL(VAL_FUNC_ARGS(value))-1; // not self
	if (argc > 7) Trap0(RE_BAD_COMMAND);
	val = DS_ARG(1);
	for (n = 1; n <= argc; n++, val++) {
		RXA_TYPE(&frm, n) = Reb_To_RXT[VAL_TYPE(val)];
		frm.args[n] = Value_To_RXI(val);
	}

	// Call the command:
	n = ext->call(cmd, &frm, 0);
	val = DS_RETURN;
	switch (n) {
	case RXR_VALUE:
		RXI_To_Value(val, frm.args[1], RXA_TYPE(&frm, 1));
		break;
	case RXR_BLOCK:
		RXI_To_Block(&frm, val);
		break;
	case RXR_UNSET:
		SET_UNSET(val);
		break;
	case RXR_NONE:
		SET_NONE(val);
		break;
	case RXR_TRUE:
		SET_TRUE(val);
		break;
	case RXR_FALSE:
		SET_FALSE(val);
		break;
	case RXR_ERROR:
	default:
		SET_UNSET(val);
	}
}
예제 #2
0
파일: c-error.c 프로젝트: 51weekend/r3
*/	REBINT Check_Error(REBVAL *val)
/*
**		Process a loop exceptions. Pass in the TOS value, returns:
**
**			 2 - if break/return, change val to that set by break
**			 1 - if break
**			-1 - if continue, change val to unset
**			 0 - if not break or continue
**			else: error if not an ERROR value
**
***********************************************************************/
{
	// It's UNSET, not an error:
	if (!IS_ERROR(val))
		Trap0(RE_NO_RETURN); //!!! change to special msg

	// If it's a BREAK, check for /return value:
	if (IS_BREAK(val)) {
		if (VAL_ERR_VALUE(val)) {
			*val = *VAL_ERR_VALUE(val);
			return 2;
		} else {
			SET_UNSET(val);
			return 1;
		}
	}

	if (IS_CONTINUE(val)) {
		SET_UNSET(val);
		return -1;
	}

	return 0;
	// Else: Let all other errors return as values.
}
예제 #3
0
파일: c-function.c 프로젝트: draegtun/ren-c
*/	void Do_Function(REBVAL *func)
/*
***********************************************************************/
{
	REBVAL *result;
	REBVAL *ds;

#if !defined(NDEBUG)
	const REBYTE *name = Get_Word_Name(DSF_LABEL(DSF));
#endif

	Eval_Functions++;

	//Dump_Block(VAL_FUNC_BODY(func));
	result = Do_Blk(VAL_FUNC_BODY(func), 0);
	ds = DS_OUT;

	if (IS_ERROR(result) && IS_RETURN(result)) {
		// Value below is kept safe from GC because no-allocation is
		// done between point of SET_THROW and here.
		if (VAL_ERR_VALUE(result))
			*ds = *VAL_ERR_VALUE(result);
		else
			SET_UNSET(ds);
	}
	else *ds = *result; // Set return value (atomic)
}
예제 #4
0
파일: c-frame.c 프로젝트: kealist/ren-c
*/  REBVAL *Append_Frame(REBSER *frame, REBVAL *word, REBCNT sym)
/*
**      Append a word to the frame word list. Expands the list
**      if necessary. Returns the value cell for the word. (Set to
**      UNSET by default to avoid GC corruption.)
**
**      If word is not NULL, use the word sym and bind the word value,
**      otherwise use sym.
**
***********************************************************************/
{
	REBSER *words = FRM_WORD_SERIES(frame);
	REBVAL *value;

	// Add to word list:
	EXPAND_SERIES_TAIL(words, 1);
	value = BLK_LAST(words);
	Val_Init_Word_Typed(value, REB_WORD, word ? VAL_WORD_SYM(word) : sym, ALL_64);
	BLK_TERM(words);

	// Bind the word to this frame:
	if (word) {
		VAL_WORD_FRAME(word) = frame;
		VAL_WORD_INDEX(word) = frame->tail;
	}

	// Add unset value to frame:
	EXPAND_SERIES_TAIL(frame, 1);
	word = BLK_LAST(frame);
	SET_UNSET(word);
	BLK_TERM(frame);

	return word; // The value cell for word.
}
예제 #5
0
파일: c-do.c 프로젝트: kjanz1899/ren-c
//
//  Do_Array_At_Core: C
//
// Most common case of evaluator invocation in Rebol: the data lives in an
// array series.  Generic routine takes flags and may act as either a DO
// or a DO/NEXT at the position given.  Option to provide an element that
// may not be resident in the array to kick off the execution.
//
REBIXO Do_Array_At_Core(
    REBVAL *out,
    const REBVAL *opt_first,
    REBARR *array,
    REBCNT index,
    REBFLGS flags
) {
    struct Reb_Frame f;

    if (opt_first) {
        f.value = opt_first;
        f.indexor = index;
    }
    else {
        // Do_Core() requires caller pre-seed first value, always
        //
        f.value = ARR_AT(array, index);
        f.indexor = index + 1;
    }

    if (IS_END(f.value)) {
        SET_UNSET(out);
        return END_FLAG;
    }

    f.out = out;
    f.source.array = array;
    f.flags = flags;
    f.mode = CALL_MODE_GUARD_ARRAY_ONLY;

    Do_Core(&f);

    return f.indexor;
}
예제 #6
0
파일: c-function.c 프로젝트: draegtun/ren-c
*/	void Do_Native(REBVAL *func)
/*
***********************************************************************/
{
	REBVAL *ds;
	REBINT n;

#if !defined(NDEBUG)
	const REBYTE *fname = Get_Word_Name(DSF_LABEL(DSF));
#endif

	Eval_Natives++;

	if ((n = VAL_FUNC_CODE(func)(DS_OUT))) {
		ds = DS_OUT;
		switch (n) {
		case R_OUT: // for compiler opt
			break;
		case R_TOS:
			*ds = *DS_TOP;
			break;
		case R_TOS1:
			*ds = *DS_NEXT;
			break;
		case R_NONE:
			SET_NONE(ds);
			break;
		case R_UNSET:
			SET_UNSET(ds);
			break;
		case R_TRUE:
			SET_TRUE(ds);
			break;
		case R_FALSE:
			SET_FALSE(ds);
			break;
		case R_ARG1:
			*ds = *D_ARG(1);
			break;
		case R_ARG2:
			*ds = *D_ARG(2);
			break;
		case R_ARG3:
			*ds = *D_ARG(3);
			break;
		}
	}
}
예제 #7
0
*/	void Do_Native(REBVAL *func)
/*
***********************************************************************/
{
	REBVAL *ds;
	REBINT n;
#ifdef DEBUGGING
	REBYTE *fname = Get_Word_Name(DSF_WORD(DSF));	// for DEBUG
	Debug_Str(fname);
#endif

	Eval_Natives++;

	if (NZ(n = VAL_FUNC_CODE(func)(DS_RETURN))) {
		ds = DS_RETURN;
		switch (n) {
		case R_RET: // for compiler opt
			break;
		case R_TOS:
			*ds = *DS_TOP;
			break;
		case R_TOS1:
			*ds = *DS_NEXT;
			break;
		case R_NONE:
			SET_NONE(ds);
			break;
		case R_UNSET:
			SET_UNSET(ds);
			break;
		case R_TRUE:
			SET_TRUE(ds);
			break;
		case R_FALSE:
			SET_FALSE(ds);
			break;
		case R_ARG1:
			*ds = *D_ARG(1);
			break;
		case R_ARG2:
			*ds = *D_ARG(2);
			break;
		case R_ARG3:
			*ds = *D_ARG(3);
			break;
		}
	}
}
예제 #8
0
파일: c-function.c 프로젝트: draegtun/ren-c
*/	void Do_Act(REBVAL *ds, REBCNT type, REBCNT act)
/*
***********************************************************************/
{
	REBACT action;
	REBINT ret;

	action = Value_Dispatch[type];
	//assert(action != 0, RP_NO_ACTION);
	if (!action) Trap_Action(type, act);
	ret = action(ds, act);
	if (ret > 0) {
		ds = DS_OUT;
		switch (ret) {
		case R_OUT: // for compiler opt
			break;
		case R_TOS:
			*ds = *DS_TOP;
			break;
		case R_TOS1:
			*ds = *DS_NEXT;
			break;
		case R_NONE:
			SET_NONE(ds);
			break;
		case R_UNSET:
			SET_UNSET(ds);
			break;
		case R_TRUE:
			SET_TRUE(ds);
			break;
		case R_FALSE:
			SET_FALSE(ds);
			break;
		case R_ARG1:
			*ds = *D_ARG(1);
			break;
		case R_ARG2:
			*ds = *D_ARG(2);
			break;
		case R_ARG3:
			*ds = *D_ARG(3);
			break;
		}
	}
}
예제 #9
0
*/	void Do_Closure(REBVAL *func)
/*
**		Do a closure by cloning its body and binding it to
**		a new frame of words/values.
**
**		This could be made faster by pre-binding the body,
**		then using Rebind_Block to rebind the words in it.
**
***********************************************************************/
{
	REBSER *body;
	REBSER *frame;
	REBVAL *result;
	REBVAL *ds;

	Eval_Functions++;
	//DISABLE_GC;

	// Clone the body of the function to allow rebinding to it:
	body = Clone_Block(VAL_FUNC_BODY(func));

	// Copy stack frame args as the closure object (one extra at head)
	frame = Copy_Values(BLK_SKIP(DS_Series, DS_ARG_BASE), SERIES_TAIL(VAL_FUNC_ARGS(func)));
	SET_FRAME(BLK_HEAD(frame), 0, VAL_FUNC_ARGS(func));

	// Rebind the body to the new context (deeply):
	//Rebind_Block(VAL_FUNC_ARGS(func), frame, body);
	Bind_Block(frame, BLK_HEAD(body), BIND_DEEP); // | BIND_NO_SELF);

	ds = DS_RETURN;
	SET_OBJECT(ds, body); // keep it GC safe
	result = Do_Blk(body, 0); // GC-OK - also, result returned on DS stack
	ds = DS_RETURN;

	if (IS_ERROR(result) && IS_RETURN(result)) {
		// Value below is kept safe from GC because no-allocation is
		// done between point of SET_THROW and here.
		if (VAL_ERR_VALUE(result))
			*ds = *VAL_ERR_VALUE(result);
		else
			SET_UNSET(ds);
	}
	else *ds = *result; // Set return value (atomic)
}
예제 #10
0
파일: c-frame.c 프로젝트: dailybarid/rebol
*/  REBVAL *Append_Frame(REBSER *frame, REBVAL *word, REBCNT sym)
/*
**      Append a word to the frame word list. Expands the list
**      if necessary. Returns the value cell for the word. (Set to
**      UNSET by default to avoid GC corruption.)
**
**      If word is not NULL, use the word sym and bind the word value,
**      otherwise use sym.
**
**      WARNING: Invalidates pointers to values within the frame
**      because the frame block may get expanded. (Use indexes.)
**
***********************************************************************/
{
	REBSER *words = FRM_WORD_SERIES(frame);
	REBVAL *value;

	// Add to word list:
	EXPAND_SERIES_TAIL(words, 1);
	value = BLK_LAST(words);
	if (word) Init_Frame_Word(value, VAL_WORD_SYM(word));
	else Init_Frame_Word(value, sym);
	BLK_TERM(words);

	// Bind the word to this frame:
	if (word) {
		VAL_WORD_FRAME(word) = frame;
		VAL_WORD_INDEX(word) = frame->tail;
	}

	// Add unset value to frame:
	EXPAND_SERIES_TAIL(frame, 1);
	word = BLK_LAST(frame);
	SET_UNSET(word);
	BLK_TERM(frame);

	return word; // The value cell for word.
}
예제 #11
0
파일: n-loop.c 프로젝트: kealist/ren-c
*/	REBOOL Loop_Throw_Should_Return(REBVAL *val)
/*
**		Process values thrown during loop, and tell the loop whether
**		to take that processed value and return it up the stack.
**		If not, then the throw was a continue...and the code
**		should just keep going.
**
**		Note: This modifies the input value.  If it returns FALSE
**		then the value is guaranteed to not be THROWN(), but it
**		may-or-may-not be THROWN() if TRUE is returned.
**
***********************************************************************/
{
	assert(THROWN(val));

	// Using words for BREAK and CONTINUE to parallel old VAL_ERR_SYM()
	// code.  So if the throw wasn't a word it can't be either of those,
	// hence the loop doesn't handle it and needs to bubble up the THROWN()
	if (!IS_WORD(val))
		return TRUE;

	// If it's a CONTINUE then wipe out the THROWN() value with UNSET,
	// and tell the loop it doesn't have to return.
	if (VAL_WORD_SYM(val) == SYM_CONTINUE) {
		SET_UNSET(val);
		return FALSE;
	}

	// If it's a BREAK, get the /WITH value (UNSET! if no /WITH) and
	// say it should be returned.
	if (VAL_WORD_SYM(val) == SYM_BREAK) {
		TAKE_THROWN_ARG(val, val);
		return TRUE;
	}

	// Else: Let all other THROWN() values bubble up.
	return TRUE;
}
예제 #12
0
파일: c-frame.c 프로젝트: dailybarid/rebol
*/	void Resolve_Context(REBSER *target, REBSER *source, REBVAL *only_words, REBFLG all, REBFLG expand)
/*
**		Only_words can be a block of words or an index in the target
**		(for new words).
**
***********************************************************************/
{
	REBINT *binds  = WORDS_HEAD(Bind_Table); // GC safe to do here
	REBVAL *words;
	REBVAL *vals;
	REBINT n;
	REBINT m;
	REBCNT i = 0;

	CHECK_BIND_TABLE;

	if (IS_PROTECT_SERIES(target)) Trap0(RE_PROTECTED);

	if (IS_INTEGER(only_words)) { // Must be: 0 < i <= tail
		i = VAL_INT32(only_words); // never <= 0
		if (i == 0) i = 1;
		if (i >= target->tail) return;
	}

	Collect_Start(BIND_NO_SELF);  // DO NOT TRAP IN THIS SECTION

	n = 0;

	// If limited resolve, tag the word ids that need to be copied:
	if (i) {
		// Only the new words of the target:
		for (words = FRM_WORD(target, i); NOT_END(words); words++)
			binds[VAL_BIND_CANON(words)] = -1;
		n = SERIES_TAIL(target) - 1;
	}
	else if (IS_BLOCK(only_words)) {
		// Limit exports to only these words:
		for (words = VAL_BLK_DATA(only_words); NOT_END(words); words++) {
			if (IS_WORD(words) || IS_SET_WORD(words)) {
				binds[VAL_WORD_CANON(words)] = -1;
				n++;
			}
		}
	}

	// Expand target as needed:
	if (expand && n > 0) {
		// Determine how many new words to add:
		for (words = FRM_WORD(target, 1); NOT_END(words); words++)
			if (binds[VAL_BIND_CANON(words)]) n--;
		// Expand frame by the amount required:
		if (n > 0) Expand_Frame(target, n, 0);
		else expand = 0;
	}

	// Maps a word to its value index in the source context.
	// Done by marking all source words (in bind table):
	words = FRM_WORDS(source)+1;
	for (n = 1; NOT_END(words); n++, words++) {
		if (IS_NONE(only_words) || binds[VAL_BIND_CANON(words)])
			binds[VAL_WORD_CANON(words)] = n;
	}

	// Foreach word in target, copy the correct value from source:
	n = i ? i : 1;
	vals = FRM_VALUE(target, n);
	for (words = FRM_WORD(target, n); NOT_END(words); words++, vals++) {
		if ((m = binds[VAL_BIND_CANON(words)])) {
			binds[VAL_BIND_CANON(words)] = 0; // mark it as set
			if (!VAL_PROTECTED(words) && (all || IS_UNSET(vals))) {
				if (m < 0) SET_UNSET(vals); // no value in source context
				else *vals = *FRM_VALUE(source, m);
				//Debug_Num("type:", VAL_TYPE(vals));
				//Debug_Str(Get_Word_Name(words));
			}
		}
	}

	// Add any new words and values:
	if (expand) {
		REBVAL *val;
		words = FRM_WORDS(source)+1;
		for (n = 1; NOT_END(words); n++, words++) {
			if (binds[VAL_BIND_CANON(words)]) {
				// Note: no protect check is needed here
				binds[VAL_BIND_CANON(words)] = 0;
				val = Append_Frame(target, 0, VAL_BIND_SYM(words));
				*val = *FRM_VALUE(source, n);
			}
		}
	}
	else {
		// Reset bind table (do not use Collect_End):
		if (i) {
			for (words = FRM_WORD(target, i); NOT_END(words); words++)
				binds[VAL_BIND_CANON(words)] = 0;
		}
		else if (IS_BLOCK(only_words)) {
			for (words = VAL_BLK_DATA(only_words); NOT_END(words); words++) {
				if (IS_WORD(words) || IS_SET_WORD(words)) binds[VAL_WORD_CANON(words)] = 0;
			}
		}
		else {
			for (words = FRM_WORDS(source)+1; NOT_END(words); words++)
				binds[VAL_BIND_CANON(words)] = 0;
		}
	}

	CHECK_BIND_TABLE;

	RESET_TAIL(BUF_WORDS);  // allow reuse, trapping ok now
}
예제 #13
0
파일: kbd.c 프로젝트: etel/ponyos
int kbd_scancode(key_event_state_t * state, unsigned char c, key_event_t * event) {
	/* Convert scancodes to a series of keys */

	event->keycode   = 0;
	event->action    = 0;
	event->modifiers = 0;
	event->key       = 0;

#if DEBUG_SCANCODES
	fprintf(stderr, "[%d] %d\n", state->kbd_s_state, (int)c);
#endif

	event->modifiers |= state->kl_ctrl  ? KEY_MOD_LEFT_CTRL   : 0;
	event->modifiers |= state->kl_shift ? KEY_MOD_LEFT_SHIFT  : 0;
	event->modifiers |= state->kl_alt   ? KEY_MOD_LEFT_ALT    : 0;
	event->modifiers |= state->kl_super ? KEY_MOD_LEFT_SUPER  : 0;

	event->modifiers |= state->kr_ctrl  ? KEY_MOD_RIGHT_CTRL  : 0;
	event->modifiers |= state->kr_shift ? KEY_MOD_RIGHT_SHIFT : 0;
	event->modifiers |= state->kr_alt   ? KEY_MOD_RIGHT_ALT   : 0;
	event->modifiers |= state->kr_super ? KEY_MOD_RIGHT_SUPER : 0;

	if (!state->kbd_s_state) {
		if (c == 0xE0) {
			state->kbd_s_state = 1;
			/* Literally nothing */
			return 0;
		}

		if (c & KEY_UP_MASK) {
			c ^= KEY_UP_MASK;
			event->action = KEY_ACTION_UP;
		} else {
			event->action = KEY_ACTION_DOWN;
		}
		int down = (event->action == KEY_ACTION_DOWN);

		switch (key_method[c]) {
			case norm:
				{
					event->keycode = kbd_us[c];
					if (state->k_ctrl) {
						int out = (int)(kbd_us_l2[c] - KEY_CTRL_MASK);
						if (out < 0 || out > 0x1F) {
							event->key = kbd_us[c];
						} else {
							event->key = out;
						}
					} else {
						event->key = state->k_shift ? kbd_us_l2[c] : kbd_us[c];
					}
				}
				break;
			case spec:
				switch (c) {
					case 0x01:
						event->key     = '\033';
						event->keycode = KEY_ESCAPE;
						break;
					case 0x1D:
						state->k_ctrl   = down;
						state->kl_ctrl  = down;
						event->keycode  = KEY_LEFT_CTRL;
						SET_UNSET(event->modifiers, KEY_MOD_LEFT_CTRL, down);
						break;
					case 0x2A:
						state->k_shift  = down;
						state->kl_shift = down;
						event->keycode  = KEY_LEFT_SHIFT;
						SET_UNSET(event->modifiers, KEY_MOD_LEFT_SHIFT, down);
						break;
					case 0x36:
						state->k_shift  = down;
						state->kr_shift = down;
						event->keycode  = KEY_RIGHT_SHIFT;
						SET_UNSET(event->modifiers, KEY_MOD_RIGHT_SHIFT, down);
						break;
					case 0x38:
						state->k_alt    = down;
						state->kl_alt   = down;
						event->keycode  = KEY_LEFT_ALT;
						SET_UNSET(event->modifiers, KEY_MOD_LEFT_ALT, down);
						break;
					default:
						break;
				}
				break;
			case func:
				switch (c) {
					case KEY_SCANCODE_F1:
						event->keycode = KEY_F1;
						break;
					case KEY_SCANCODE_F2:
						event->keycode = KEY_F2;
						break;
					case KEY_SCANCODE_F3:
						event->keycode = KEY_F3;
						break;
					case KEY_SCANCODE_F4:
						event->keycode = KEY_F4;
						break;
					case KEY_SCANCODE_F5:
						event->keycode = KEY_F5;
						break;
					case KEY_SCANCODE_F6:
						event->keycode = KEY_F6;
						break;
					case KEY_SCANCODE_F7:
						event->keycode = KEY_F7;
						break;
					case KEY_SCANCODE_F8:
						event->keycode = KEY_F8;
						break;
					case KEY_SCANCODE_F9:
						event->keycode = KEY_F9;
						break;
					case KEY_SCANCODE_F10:
						event->keycode = KEY_F10;
						break;
					case KEY_SCANCODE_F11:
						event->keycode = KEY_F11;
						break;
					case KEY_SCANCODE_F12:
						event->keycode = KEY_F12;
						break;
				}
				break;
			default:
				break;
		}

		if (event->key) {
			return down;
		}

		return 0;
	} else if (state->kbd_s_state == 1) {

		if (c & KEY_UP_MASK) {
			c ^= KEY_UP_MASK;
			event->action = KEY_ACTION_UP;
		} else {
			event->action = KEY_ACTION_DOWN;
		}

		int down = (event->action == KEY_ACTION_DOWN);
		switch (c) {
			case 0x5B:
				state->k_super  = down;
				state->kl_super = down;
				event->keycode  = KEY_LEFT_SUPER;
				SET_UNSET(event->modifiers, KEY_MOD_LEFT_SUPER, down);
				break;
			case 0x5C:
				state->k_super  = down;
				state->kr_super = down;
				event->keycode  = KEY_RIGHT_SUPER;
				SET_UNSET(event->modifiers, KEY_MOD_RIGHT_SUPER, down);
				break;
			case 0x1D:
				state->kr_ctrl  = down;
				state->k_ctrl   = down;
				event->keycode  = KEY_RIGHT_CTRL;
				SET_UNSET(event->modifiers, KEY_MOD_RIGHT_CTRL, down);
				break;
			case 0x38:
				state->kr_alt   = down;
				state->k_alt    = down;
				event->keycode  = KEY_RIGHT_ALT;
				SET_UNSET(event->modifiers, KEY_MOD_RIGHT_ALT, down);
				break;
			case 0x48:
				event->keycode = KEY_ARROW_UP;
				break;
			case 0x4D:
				event->keycode = KEY_ARROW_RIGHT;
				break;
			case 0x50:
				event->keycode = KEY_ARROW_DOWN;
				break;
			case 0x4B:
				event->keycode = KEY_ARROW_LEFT;
				break;
			case 0x49:
				event->keycode = KEY_PAGE_UP;
				break;
			case 0x51:
				event->keycode = KEY_PAGE_DOWN;
				break;
			default:
				break;
		}

		state->kbd_s_state = 0;
		return 0;
	}
	return 0;
}
예제 #14
0
파일: f-extension.c 프로젝트: MannyZhong/r3
*/	void Do_Commands(REBSER *cmds, void *context)
/*
**		Evaluate a block of commands as efficiently as possible.
**		The arguments to each command must already be reduced or
**		use only variable lookup.
**
**		Returns the last evaluated value, if provided.
**
***********************************************************************/
{
	REBVAL *blk;
	REBCNT index = 0;
	REBVAL *set_word = 0;
	REBVAL *cmd_word;
	REBSER *words;
	REBVAL *args;
	REBVAL *val;
	REBVAL *func;
	RXIFRM frm;	// args stored here
	REBCNT n;
	REBEXT *ext;
	REBCEC *ctx;

	if ((ctx = context)) ctx->block = cmds;
	blk = BLK_HEAD(cmds);

	while (NOT_END(blk)) {

		// var: command result
		if IS_SET_WORD(blk) {
			set_word = blk++;
			index++;
		};

		// get command function
		if (IS_WORD(cmd_word = blk)) {
			// Optimized var fetch:
			n = VAL_WORD_INDEX(blk);
			if (n > 0) func = FRM_VALUES(VAL_WORD_FRAME(blk)) + n;
			else func = Get_Var(blk); // fallback
		} else func = blk;

		if (!IS_COMMAND(func)) Trap2(RE_EXPECT_VAL, Get_Type_Word(REB_COMMAND), blk);

		// Advance to next value
		blk++;
		if (ctx) ctx->index = index; // position of function
		index++;

		// get command arguments and body
		words = VAL_FUNC_WORDS(func);
		RXA_COUNT(&frm) = SERIES_TAIL(VAL_FUNC_ARGS(func))-1; // not self

		// collect each argument (arg list already validated on MAKE)
		n = 0;
		for (args = BLK_SKIP(words, 1); NOT_END(args); args++) {

			//Debug_Type(args);
			val = blk++;
			index++;
			if (IS_END(val)) Trap2(RE_NO_ARG, cmd_word, args);
			//Debug_Type(val);

			// actual arg is a word, lookup?
			if (VAL_TYPE(val) >= REB_WORD) {
				if (IS_WORD(val)) {
					if (IS_WORD(args)) val = Get_Var(val);
				}
				else if (IS_PATH(val)) {
					if (IS_WORD(args)) val = Get_Any_Var(val); // volatile value!
				}
				else if (IS_PAREN(val)) {
					val = Do_Blk(VAL_SERIES(val), 0); // volatile value!
				}
				// all others fall through
			}

			// check datatype
			if (!TYPE_CHECK(args, VAL_TYPE(val)))
				Trap3(RE_EXPECT_ARG, cmd_word, args, Of_Type(val));

			// put arg into command frame
			n++;
			RXA_TYPE(&frm, n) = Reb_To_RXT[VAL_TYPE(val)];
			frm.args[n] = Value_To_RXI(val);
		}

		// Call the command (also supports different extension modules):
		func  = BLK_HEAD(VAL_FUNC_BODY(func));
		n = (REBCNT)VAL_INT64(func + 1);
		ext = &Ext_List[VAL_I32(VAL_OBJ_VALUE(func, 1))]; // Handler
		n = ext->call(n, &frm, context);
		val = DS_RETURN;
		switch (n) {
		case RXR_VALUE:
			RXI_To_Value(val, frm.args[1], RXA_TYPE(&frm, 1));
			break;
		case RXR_BLOCK:
			RXI_To_Block(&frm, val);
			break;
		case RXR_UNSET:
			SET_UNSET(val);
			break;
		case RXR_NONE:
			SET_NONE(val);
			break;
		case RXR_TRUE:
			SET_TRUE(val);
			break;
		case RXR_FALSE:
			SET_FALSE(val);
			break;
		case RXR_ERROR:
		default:
			SET_UNSET(val);
		}

		if (set_word) {
			Set_Var(set_word, val);
			set_word = 0;
		}
	}
}
예제 #15
0
파일: n-loop.c 프로젝트: kealist/ren-c
*/	static REB_R Loop_Each(struct Reb_Call *call_, LOOP_MODE mode)
/*
**		Common implementation code of FOR-EACH, REMOVE-EACH, MAP-EACH,
**		and EVERY.
**
***********************************************************************/
{
	REBSER *body;
	REBVAL *vars;
	REBVAL *words;
	REBSER *frame;

	// `data` is the series/object/map/etc. being iterated over
	// Note: `data_is_object` flag is optimized out, but hints static analyzer
	REBVAL *data = D_ARG(2);
	REBSER *series;
	const REBOOL data_is_object = ANY_OBJECT(data);

	REBSER *out;	// output block (needed for MAP-EACH)

	REBINT index;	// !!!! should these be REBCNT?
	REBINT tail;
	REBINT windex;	// write
	REBINT rindex;	// read
	REBOOL break_with = FALSE;
	REBOOL every_true = TRUE;
	REBCNT i;
	REBCNT j;
	REBVAL *ds;

	if (IS_NONE(data)) return R_NONE;

	body = Init_Loop(D_ARG(1), D_ARG(3), &frame); // vars, body
	Val_Init_Object(D_ARG(1), frame); // keep GC safe
	Val_Init_Block(D_ARG(3), body); // keep GC safe

	SET_NONE(D_OUT); // Default result to NONE if the loop does not run

	if (mode == LOOP_MAP_EACH) {
		// Must be managed *and* saved...because we are accumulating results
		// into it, and those results must be protected from GC

		// !!! This means we cannot Free_Series in case of a BREAK, we
		// have to leave it to the GC.  Should there be a variant which
		// lets a series be a GC root for a temporary time even if it is
		// not SER_KEEP?

		out = Make_Array(VAL_LEN(data));
		MANAGE_SERIES(out);
		SAVE_SERIES(out);
	}

	// Get series info:
	if (data_is_object) {
		series = VAL_OBJ_FRAME(data);
		out = FRM_WORD_SERIES(series); // words (the out local reused)
		index = 1;
		//if (frame->tail > 3) raise Error_Invalid_Arg(FRM_WORD(frame, 3));
	}
	else if (IS_MAP(data)) {
		series = VAL_SERIES(data);
		index = 0;
		//if (frame->tail > 3) raise Error_Invalid_Arg(FRM_WORD(frame, 3));
	}
	else {
		series = VAL_SERIES(data);
		index  = VAL_INDEX(data);
		if (index >= cast(REBINT, SERIES_TAIL(series))) {
			if (mode == LOOP_REMOVE_EACH) {
				SET_INTEGER(D_OUT, 0);
			}
			else if (mode == LOOP_MAP_EACH) {
				UNSAVE_SERIES(out);
				Val_Init_Block(D_OUT, out);
			}
			return R_OUT;
		}
	}

	windex = index;

	// Iterate over each value in the data series block:
	while (index < (tail = SERIES_TAIL(series))) {

		rindex = index;  // remember starting spot
		j = 0;

		// Set the FOREACH loop variables from the series:
		for (i = 1; i < frame->tail; i++) {

			vars = FRM_VALUE(frame, i);
			words = FRM_WORD(frame, i);

			// var spec is WORD
			if (IS_WORD(words)) {

				if (index < tail) {

					if (ANY_BLOCK(data)) {
						*vars = *BLK_SKIP(series, index);
					}
					else if (data_is_object) {
						if (!VAL_GET_EXT(BLK_SKIP(out, index), EXT_WORD_HIDE)) {
							// Alternate between word and value parts of object:
							if (j == 0) {
								Val_Init_Word(vars, REB_WORD, VAL_WORD_SYM(BLK_SKIP(out, index)), series, index);
								if (NOT_END(vars+1)) index--; // reset index for the value part
							}
							else if (j == 1)
								*vars = *BLK_SKIP(series, index);
							else
								raise Error_Invalid_Arg(words);
							j++;
						}
						else {
							// Do not evaluate this iteration
							index++;
							goto skip_hidden;
						}
					}
					else if (IS_VECTOR(data)) {
						Set_Vector_Value(vars, series, index);
					}
					else if (IS_MAP(data)) {
						REBVAL *val = BLK_SKIP(series, index | 1);
						if (!IS_NONE(val)) {
							if (j == 0) {
								*vars = *BLK_SKIP(series, index & ~1);
								if (IS_END(vars+1)) index++; // only words
							}
							else if (j == 1)
								*vars = *BLK_SKIP(series, index);
							else
								raise Error_Invalid_Arg(words);
							j++;
						}
						else {
							index += 2;
							goto skip_hidden;
						}
					}
					else { // A string or binary
						if (IS_BINARY(data)) {
							SET_INTEGER(vars, (REBI64)(BIN_HEAD(series)[index]));
						}
						else if (IS_IMAGE(data)) {
							Set_Tuple_Pixel(BIN_SKIP(series, index), vars);
						}
						else {
							VAL_SET(vars, REB_CHAR);
							VAL_CHAR(vars) = GET_ANY_CHAR(series, index);
						}
					}
					index++;
				}
				else SET_NONE(vars);
			}
			// var spec is SET_WORD:
			else if (IS_SET_WORD(words)) {
				if (ANY_OBJECT(data) || IS_MAP(data))
					*vars = *data;
				else
					Val_Init_Block_Index(vars, series, index);

				//if (index < tail) index++; // do not increment block.
			}
			else
				raise Error_Invalid_Arg(words);
		}

		if (index == rindex) {
			// the word block has only set-words: for-each [a:] [1 2 3][]
			index++;
		}

		if (Do_Block_Throws(D_OUT, body, 0)) {
			if (IS_WORD(D_OUT) && VAL_WORD_SYM(D_OUT) == SYM_CONTINUE) {
				if (mode == LOOP_REMOVE_EACH) {
					// signal the post-body-execution processing that we
					// *do not* want to remove the element on a CONTINUE
					SET_FALSE(D_OUT);
				}
				else {
					// CONTINUE otherwise acts "as if" the loop body execution
					// returned an UNSET!
					SET_UNSET(D_OUT);
				}
			}
			else if (IS_WORD(D_OUT) && VAL_WORD_SYM(D_OUT) == SYM_BREAK) {
				// If it's a BREAK, get the /WITH value (UNSET! if no /WITH)
				// Though technically this doesn't really tell us if a
				// BREAK/WITH happened, as you can BREAK/WITH an UNSET!
				TAKE_THROWN_ARG(D_OUT, D_OUT);
				if (!IS_UNSET(D_OUT))
					break_with = TRUE;
				index = rindex;
				break;
			}
			else {
				// Any other kind of throw, with a WORD! name or otherwise...
				index = rindex;
				break;
			}
		}

		switch (mode) {
		case LOOP_FOR_EACH:
			// no action needed after body is run
			break;
		case LOOP_REMOVE_EACH:
			// If FALSE return, copy values to the write location
			// !!! Should UNSET! also act as conditional false here?  Error?
			if (IS_CONDITIONAL_FALSE(D_OUT)) {
				REBYTE wide = SERIES_WIDE(series);
				// memory areas may overlap, so use memmove and not memcpy!

				// !!! This seems a slow way to do it, but there's probably
				// not a lot that can be done as the series is expected to
				// be in a good state for the next iteration of the body. :-/
				memmove(
					series->data + (windex * wide),
					series->data + (rindex * wide),
					(index - rindex) * wide
				);
				windex += index - rindex;
			}
			break;
		case LOOP_MAP_EACH:
			// anything that's not an UNSET! will be added to the result
			if (!IS_UNSET(D_OUT)) Append_Value(out, D_OUT);
			break;
		case LOOP_EVERY:
			if (every_true) {
				// !!! This currently treats UNSET! as true, which ALL
				// effectively does right now.  That's likely a bad idea.
				// When ALL changes, so should this.
				//
				every_true = IS_CONDITIONAL_TRUE(D_OUT);
			}
			break;
		default:
			assert(FALSE);
		}
skip_hidden: ;
	}

	switch (mode) {
	case LOOP_FOR_EACH:
		// Nothing to do but return last result (will be UNSET! if an
		// ordinary BREAK was used, the /WITH if a BREAK/WITH was used,
		// and an UNSET! if the last loop iteration did a CONTINUE.)
		return R_OUT;

	case LOOP_REMOVE_EACH:
		// Remove hole (updates tail):
		if (windex < index) Remove_Series(series, windex, index - windex);
		SET_INTEGER(D_OUT, index - windex);

		return R_OUT;

	case LOOP_MAP_EACH:
		UNSAVE_SERIES(out);
		if (break_with) {
			// If BREAK is given a /WITH parameter that is not an UNSET!, it
			// is assumed that you want to override the accumulated mapped
			// data so far and return the /WITH value. (which will be in
			// D_OUT when the loop above is `break`-ed)

			// !!! Would be nice if we could Free_Series(out), but it is owned
			// by GC (we had to make it that way to use SAVE_SERIES on it)
			return R_OUT;
		}

		// If you BREAK/WITH an UNSET! (or just use a BREAK that has no
		// /WITH, which is indistinguishable in the thrown value) then it
		// returns the accumulated results so far up to the break.

		Val_Init_Block(D_OUT, out);
		return R_OUT;

	case LOOP_EVERY:
		// Result is the cumulative TRUE? state of all the input (with any
		// unsets taken out of the consideration).  The last TRUE? input
		// if all valid and NONE! otherwise.  (Like ALL.)  If the loop
		// never runs, `every_true` will be TRUE *but* D_OUT will be NONE!
		if (!every_true)
			SET_NONE(D_OUT);
		return R_OUT;
	}

	DEAD_END;
}