Beispiel #1
0
static REBOOL Equal_Object(REBVAL *val, REBVAL *arg)
{
	REBSER *f1;
	REBSER *f2;
	REBSER *w1;
	REBSER *w2;
	REBINT n;

	if (VAL_TYPE(arg) != VAL_TYPE(val)) return FALSE;

	f1 = VAL_OBJ_FRAME(val);
	f2 = VAL_OBJ_FRAME(arg);
	if (f1 == f2) return TRUE;
	if (f1->tail != f2->tail) return FALSE;

	w1 = FRM_WORD_SERIES(f1);
	w2 = FRM_WORD_SERIES(f2);
	if (w1->tail != w2->tail) return FALSE;

	// Compare each entry:
	for (n = 1; n < (REBINT)(f1->tail); n++) {
		if (Cmp_Value(BLK_SKIP(w1, n), BLK_SKIP(w2, n), FALSE)) return FALSE;
		// Use Compare_Values();
		if (Cmp_Value(BLK_SKIP(f1, n), BLK_SKIP(f2, n), FALSE)) return FALSE;
	}

	return TRUE;
}
Beispiel #2
0
*/	static REBSER *Init_Loop(REBVAL *spec, REBVAL *body_blk, REBSER **fram)
/*
**		Initialize standard for loops (copy block, make frame, bind).
**		Spec: WORD or [WORD ...]
**
***********************************************************************/
{
	REBSER *frame;
	REBINT len;
	REBVAL *word;
	REBVAL *vals;
	REBSER *body;

	// For :WORD format, get the var's value:
	if (IS_GET_WORD(spec)) spec = Get_Var(spec);

	// Hand-make a FRAME (done for for speed):
	len = IS_BLOCK(spec) ? VAL_LEN(spec) : 1;
	if (len == 0) Trap_Arg(spec);
	frame = Make_Frame(len);
	SET_SELFLESS(frame);
	SERIES_TAIL(frame) = len+1;
	SERIES_TAIL(FRM_WORD_SERIES(frame)) = len+1;

	// Setup for loop:
	word = FRM_WORD(frame, 1); // skip SELF
	vals = BLK_SKIP(frame, 1);
	if (IS_BLOCK(spec)) spec = VAL_BLK_DATA(spec);

	// Optimally create the FOREACH frame:
	while (len-- > 0) {
		if (!IS_WORD(spec) && !IS_SET_WORD(spec)) {
			// Prevent inconsistent GC state:
			Free_Series(FRM_WORD_SERIES(frame));
			Free_Series(frame);
			Trap_Arg(spec);
		}
		VAL_SET(word, VAL_TYPE(spec));
		VAL_BIND_SYM(word) = VAL_WORD_SYM(spec);
		VAL_BIND_TYPESET(word) = ALL_64;
		word++;
		SET_NONE(vals);
		vals++;
		spec++;
	}
	SET_END(word);
	SET_END(vals);

	body = Clone_Block_Value(body_blk);
	Bind_Block(frame, BLK_HEAD(body), BIND_DEEP);

	*fram = frame;

	return body;
}
Beispiel #3
0
*/	static REBSER *Init_Loop(const REBVAL *spec, REBVAL *body_blk, REBSER **fram)
/*
**		Initialize standard for loops (copy block, make frame, bind).
**		Spec: WORD or [WORD ...]
**
***********************************************************************/
{
	REBSER *frame;
	REBINT len;
	REBVAL *word;
	REBVAL *vals;
	REBSER *body;

	// For :WORD format, get the var's value:
	if (IS_GET_WORD(spec)) spec = GET_VAR(spec);

	// Hand-make a FRAME (done for for speed):
	len = IS_BLOCK(spec) ? VAL_LEN(spec) : 1;
	if (len == 0) raise Error_Invalid_Arg(spec);
	frame = Make_Frame(len, FALSE);
	SERIES_TAIL(frame) = len+1;
	SERIES_TAIL(FRM_WORD_SERIES(frame)) = len+1;

	// Setup for loop:
	word = FRM_WORD(frame, 1); // skip SELF
	vals = BLK_SKIP(frame, 1);
	if (IS_BLOCK(spec)) spec = VAL_BLK_DATA(spec);

	// Optimally create the FOREACH frame:
	while (len-- > 0) {
		if (!IS_WORD(spec) && !IS_SET_WORD(spec)) {
			// Prevent inconsistent GC state:
			Free_Series(FRM_WORD_SERIES(frame));
			Free_Series(frame);
			raise Error_Invalid_Arg(spec);
		}
		Val_Init_Word_Typed(word, VAL_TYPE(spec), VAL_WORD_SYM(spec), ALL_64);
		word++;
		SET_NONE(vals);
		vals++;
		spec++;
	}
	SET_END(word);
	SET_END(vals);

	body = Copy_Array_At_Deep_Managed(
		VAL_SERIES(body_blk), VAL_INDEX(body_blk)
	);
	Bind_Values_Deep(BLK_HEAD(body), frame);

	*fram = frame;

	return body;
}
Beispiel #4
0
*/  void Expand_Frame(REBSER *frame, REBCNT delta, REBCNT copy)
/*
**      Expand a frame. Copy words if flagged.
**
***********************************************************************/
{
	REBSER *words = FRM_WORD_SERIES(frame);

	Extend_Series(frame, delta);

	// Expand or copy WORDS block:
	if (copy) FRM_WORD_SERIES(frame) = Copy_Expand_Block(words, delta);
	else Extend_Series(words, delta);
}
Beispiel #5
0
*/  REBSER *Collect_End(REBSER *prior)
/*
**		Finish collecting words, and free the Bind_Table for reuse.
**
***********************************************************************/
{
	REBVAL *words;
	REBINT *binds = WORDS_HEAD(Bind_Table); // GC safe to do here

	// Reset binding table (note BUF_WORDS may have expanded):
	for (words = BLK_HEAD(BUF_WORDS); NOT_END(words); words++)
		binds[VAL_WORD_CANON(words)] = 0;

	// If no new words, prior frame:
	if (prior && SERIES_TAIL(BUF_WORDS) == SERIES_TAIL(prior)) {
		RESET_TAIL(BUF_WORDS);  // allow reuse
		return FRM_WORD_SERIES(prior);
	}

	prior = Copy_Series(BUF_WORDS);
	RESET_TAIL(BUF_WORDS);  // allow reuse
	BARE_SERIES(prior); // No GC ever needed for word list

	CHECK_BIND_TABLE;

	return prior;
}
Beispiel #6
0
*/  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.
}
Beispiel #7
0
*/  REBSER *Make_Object(REBSER *parent, REBVAL *block)
/*
**      Create an object from a parent object and a spec block.
**		The words within the resultant object are not bound.
**
***********************************************************************/
{
	REBSER *words;
	REBSER *object;

	PG_Reb_Stats->Objects++;

	if (!block || IS_END(block)) {
		object = parent ? Copy_Block_Values(parent, 0, SERIES_TAIL(parent), TS_CLONE) : Make_Frame(0);
	} else {
		words = Collect_Frame(BIND_ONLY, parent, block); // GC safe
		object = Create_Frame(words, 0); // GC safe
		if (parent) {
			if (Reb_Opts->watch_obj_copy)
				Debug_Fmt(BOOT_STR(RS_WATCH, 2), SERIES_TAIL(parent) - 1, FRM_WORD_SERIES(object));
			// Copy parent values and deep copy blocks and strings:
			COPY_VALUES(FRM_VALUES(parent)+1, FRM_VALUES(object)+1, SERIES_TAIL(parent) - 1);
			Copy_Deep_Values(object, 1, SERIES_TAIL(object), TS_CLONE);
		}
	}

	//Dump_Frame(object);
	return object;
}
Beispiel #8
0
static REBSER *Trim_Object(REBSER *obj)
{
	REBVAL *val;
	REBINT cnt = 0;
	REBSER *nobj;
	REBVAL *nval;
	REBVAL *word;
	REBVAL *nwrd;

	word = FRM_WORDS(obj)+1;
	for (val = FRM_VALUES(obj)+1; NOT_END(val); val++, word++) {
		if (VAL_TYPE(val) > REB_NONE && !VAL_GET_OPT(word, OPTS_HIDE))
			cnt++;
	}

	nobj = Make_Frame(cnt);
	nval = FRM_VALUES(nobj)+1;
	word = FRM_WORDS(obj)+1;
	nwrd = FRM_WORDS(nobj)+1;
	for (val = FRM_VALUES(obj)+1; NOT_END(val); val++, word++) {
		if (VAL_TYPE(val) > REB_NONE && !VAL_GET_OPT(word, OPTS_HIDE)) {
			*nval++ = *val;
			*nwrd++ = *word;
		}
	}
	SET_END(nval);
	SET_END(nwrd);
	SERIES_TAIL(nobj) = cnt+1;
	SERIES_TAIL(FRM_WORD_SERIES(nobj)) = cnt+1;

	return nobj;
}
Beispiel #9
0
*/  REBSER *Merge_Frames(REBSER *parent1, REBSER *parent2)
/*
**      Create a child frame from two parent frames. Merge common fields.
**      Values from the second parent take precedence.
**
**		Deep copy and rebind the child.
**
***********************************************************************/
{
	REBSER *wrds;
	REBSER *child;
	REBVAL *words;
	REBVAL *value;
	REBCNT n;
	REBINT *binds = WORDS_HEAD(Bind_Table);

	// Merge parent1 and parent2 words.
	// Keep the binding table.
	Collect_Start(BIND_ALL);
	// Setup binding table and BUF_WORDS with parent1 words:
	if (parent1) Collect_Object(parent1);
	// Add parent2 words to binding table and BUF_WORDS:
	Collect_Words(BLK_SKIP(FRM_WORD_SERIES(parent2), 1), BIND_ALL);

	// Allocate child (now that we know the correct size):
	wrds = Copy_Series(BUF_WORDS);
	child = Make_Block(SERIES_TAIL(wrds));
	value = Append_Value(child);
	VAL_SET(value, REB_FRAME);
	VAL_FRM_WORDS(value) = wrds;
	VAL_FRM_SPEC(value) = 0;

	// Copy parent1 values:
	COPY_VALUES(FRM_VALUES(parent1)+1, FRM_VALUES(child)+1, SERIES_TAIL(parent1)-1);

	// Copy parent2 values:
	words = FRM_WORDS(parent2)+1;
	value = FRM_VALUES(parent2)+1;
	for (; NOT_END(words); words++, value++) {
		// no need to search when the binding table is available
		n = binds[VAL_WORD_CANON(words)];
		BLK_HEAD(child)[n] = *value;
	}

	// Terminate the child frame:
	SERIES_TAIL(child) = SERIES_TAIL(wrds);
	BLK_TERM(child);

	// Deep copy the child
	Copy_Deep_Values(child, 1, SERIES_TAIL(child), TS_CLONE);

	// Rebind the child
	Rebind_Block(parent1, child, BLK_SKIP(child, 1), REBIND_FUNC);
	Rebind_Block(parent2, child, BLK_SKIP(child, 1), REBIND_FUNC | REBIND_TABLE);

	// release the bind table 
	Collect_End(wrds);

	return child;
}
Beispiel #10
0
*/  void Expand_Frame(REBSER *frame, REBCNT delta, REBCNT copy)
/*
**      Expand a frame. Copy words if flagged.
**
***********************************************************************/
{
	REBSER *words = FRM_WORD_SERIES(frame);

	Extend_Series(frame, delta);
	BLK_TERM(frame);

	// Expand or copy WORDS block:
	if (copy) {
		REBOOL managed = SERIES_GET_FLAG(FRM_WORD_SERIES(frame), SER_MANAGED);
		FRM_WORD_SERIES(frame) = Copy_Array_Extra_Shallow(words, delta);
		if (managed) MANAGE_SERIES(FRM_WORD_SERIES(frame));
	}
	else {
		Extend_Series(words, delta);
		BLK_TERM(words);
	}
}
Beispiel #11
0
*/  REBSER *Merge_Frames(REBSER *parent, REBSER *child)
/*
**      Create a frame from two frames. Merge common fields.
**      Values from the second frame take precedence. No rebinding.
**
***********************************************************************/
{
	REBSER *wrds;
	REBSER *frame;
	REBVAL *words;
	REBVAL *value;
	REBCNT n;

	// Merge parent and child words. This trick works because the
	// word list is itself a valid block.
	wrds = Collect_Frame(BIND_ALL, parent, BLK_SKIP(FRM_WORD_SERIES(child),1));

	// Allocate frame (now that we know the correct size):
	frame = Make_Block(SERIES_TAIL(wrds));  // GC!!!
	value = Append_Value(frame);
	VAL_SET(value, REB_FRAME);
	VAL_FRM_WORDS(value) = wrds;
	VAL_FRM_SPEC(value) = 0;

	// Copy parent values:
	COPY_VALUES(FRM_VALUES(parent)+1, FRM_VALUES(frame)+1, SERIES_TAIL(parent)-1);

	// Copy new words and values:
	words = FRM_WORDS(child)+1;
	value = FRM_VALUES(child)+1;
	for (; NOT_END(words); words++, value++) {
		n = Find_Word_Index(frame, VAL_BIND_SYM(words), FALSE);
		if (n) BLK_HEAD(frame)[n] = *value;
	}

	// Terminate the new frame:
	SERIES_TAIL(frame) = SERIES_TAIL(wrds);
	BLK_TERM(frame);

	return frame;
}
Beispiel #12
0
*/  REBCNT Find_Word_Index(REBSER *frame, REBCNT sym, REBFLG always)
/*
**      Search a frame looking for the given word symbol.
**      Return the frame index for a word. Locate it by matching
**      the canon word identifiers. Return 0 if not found.
**
***********************************************************************/
{
	REBCNT len = SERIES_TAIL(FRM_WORD_SERIES(frame));
	REBVAL *word = FRM_WORDS(frame) + 1;
	REBCNT n;
	REBCNT s;

	s = SYMBOL_TO_CANON(sym); // always compare to CANON sym

	for (n = 1; n < len; n++, word++)
		if (sym == VAL_BIND_SYM(word) || s == VAL_BIND_CANON(word))
			return (!always && VAL_GET_OPT(word, OPTS_HIDE)) ? 0 : n;

	return 0;
}
Beispiel #13
0
*/	REBSER *Map_To_Object(REBSER *mapser)
/*
***********************************************************************/
{
	REBVAL *val;
	REBCNT cnt = 0;
	REBSER *frame;
	REBVAL *word;
	REBVAL *mval;

	// Count number of set entries:
	for (mval = BLK_HEAD(mapser); NOT_END(mval) && NOT_END(mval+1); mval += 2) {
		if (ANY_WORD(mval) && !IS_NONE(mval+1)) cnt++;
	}

	// See Make_Frame() - cannot use it directly because no Collect_Words
	frame = Make_Frame(cnt, TRUE);

	word = FRM_WORD(frame, 1);
	val  = FRM_VALUE(frame, 1);
	for (mval = BLK_HEAD(mapser); NOT_END(mval) && NOT_END(mval+1); mval += 2) {
		if (ANY_WORD(mval) && !IS_NONE(mval+1)) {
			Init_Unword(
				word,
				REB_SET_WORD,
				VAL_WORD_SYM(mval),
				// all types except END or UNSET
				~((TYPESET(REB_END) | TYPESET(REB_UNSET)))
			);
			word++;
			*val++ = mval[1];
		}
	}

	SET_END(word);
	SET_END(val);
	FRM_WORD_SERIES(frame)->tail = frame->tail = cnt + 1;

	return frame;
}
Beispiel #14
0
*/  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.
}
Beispiel #15
0
*/	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;
}
Beispiel #16
0
*/  void Assert_Frame_Core(REBSER *frame)
/*
***********************************************************************/
{
	REBINT n;
	REBVAL *value;
	REBSER *words;
	REBVAL *word;
	REBINT tail;
	REBVAL *frame_value; // "FRAME!-typed value" at head of "frame" series

	frame_value = BLK_HEAD(frame);
	if (!IS_FRAME(frame_value)) Panic_Series(frame);

	if ((frame == VAL_SERIES(ROOT_ROOT)) || (frame == Task_Series)) {
		// !!! Currently it is allowed that the root frames not
		// have a wordlist.  This distinct behavior accomodation is
		// not worth having the variance of behavior, but since
		// it's there for now... allow it for just those two.

		if(!FRM_WORD_SERIES(frame))
			return;
	}

	value = FRM_VALUES(frame);

	words = FRM_WORD_SERIES(frame);
	word = FRM_WORDS(frame);
	tail = SERIES_TAIL(frame);

	for (n = 0; n < tail; n++, value++, word++) {
		if (n == 0) {
			if (
				VAL_WORD_SYM(word) != SYM_SELF
				&& VAL_WORD_SYM(word) != SYM_NOT_USED
			) {
				Debug_Fmt("** First slot in frame is not SELF or null symbol");
				Panic_Series(frame);
			}
		}

		if (IS_END(word) || IS_END(value)) {
			Debug_Fmt(
				"** Early %s end at index: %d",
				IS_END(word) ? "word" : "value",
				n
			);
			Panic_Series(frame);
		}

		if (!ANY_WORD(word)) {
			Debug_Fmt("** Non-word in word list, type: %d\n", VAL_TYPE(word));
			Panic_Series(words);
		}

		if (!VAL_GET_EXT(word, EXT_WORD_TYPED)) {
			Debug_Fmt("** Frame words contains non-'typed'-word");
			Panic_Series(words);
		}
	}

	if (NOT_END(word) || NOT_END(value)) {
		Debug_Fmt(
			"** Missing %s end at index: %d type: %d",
			NOT_END(word) ? "word" : "value",
			n,
			VAL_TYPE(word)
		);
		Panic_Series(frame);
	}
}
Beispiel #17
0
*/  REBSER *Make_Object(REBSER *parent, REBVAL value[])
/*
**      Create an object from a parent object and a spec block.
**		The words within the resultant object are not bound.
**
***********************************************************************/
{
	REBSER *words;
	REBSER *object;

	PG_Reb_Stats->Objects++;

	if (!value || IS_END(value)) {
		if (parent) {
			object = Copy_Array_Core_Managed(
				parent, 0, SERIES_TAIL(parent), TRUE, TS_CLONE
			);
		}
		else {
			object = Make_Frame(0, TRUE);
			MANAGE_FRAME(object);
		}
	}
	else {
		words = Collect_Frame(parent, &value[0], BIND_ONLY); // GC safe
		object = Create_Frame(words, 0); // GC safe
		if (parent) {
			if (Reb_Opts->watch_obj_copy)
				Debug_Fmt(cs_cast(BOOT_STR(RS_WATCH, 2)), SERIES_TAIL(parent) - 1, FRM_WORD_SERIES(object));

			// Bitwise copy parent values (will have bits fixed by Clonify)
			memcpy(
				FRM_VALUES(object) + 1,
				FRM_VALUES(parent) + 1,
				(SERIES_TAIL(parent) - 1) * sizeof(REBVAL)
			);

			// For values we copied that were blocks and strings, replace
			// their series components with deep copies of themselves:
			Clonify_Values_Len_Managed(
				BLK_SKIP(object, 1), SERIES_TAIL(object) - 1, TRUE, TS_CLONE
			);

			// The *word series* might have been reused from the parent,
			// based on whether any words were added, or we could have gotten
			// a fresh one back.  Force our invariant here (as the screws
			// tighten...)
			ENSURE_SERIES_MANAGED(FRM_WORD_SERIES(object));
			MANAGE_SERIES(object);
		}
		else {
			MANAGE_FRAME(object);
		}

		assert(words == FRM_WORD_SERIES(object));
	}

	ASSERT_SERIES_MANAGED(object);
	ASSERT_SERIES_MANAGED(FRM_WORD_SERIES(object));
	ASSERT_FRAME(object);
	return object;
}
Beispiel #18
0
*/	static REB_R Loop_Each(struct Reb_Call *call_, REBINT mode)
/*
**		Supports these natives (modes):
**			0: foreach
**			1: remove-each
**			2: map
**
***********************************************************************/
{
	REBSER *body;
	REBVAL *vars;
	REBVAL *words;
	REBSER *frame;
	REBVAL *value;
	REBSER *series;
	REBSER *out;	// output block (for MAP, mode = 2)

	REBINT index;	// !!!! should these be REBCNT?
	REBINT tail;
	REBINT windex;	// write
	REBINT rindex;	// read
	REBINT err;
	REBCNT i;
	REBCNT j;
	REBVAL *ds;

	assert(mode >= 0 && mode < 3);

	value = D_ARG(2); // series
	if (IS_NONE(value)) return R_NONE;

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

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

	// If it's MAP, create result block:
	if (mode == 2) {
		out = Make_Block(VAL_LEN(value));
		SAVE_SERIES(out);
	}

	// Get series info:
	if (ANY_OBJECT(value)) {
		series = VAL_OBJ_FRAME(value);
		out = FRM_WORD_SERIES(series); // words (the out local reused)
		index = 1;
		//if (frame->tail > 3) Trap_Arg_DEAD_END(FRM_WORD(frame, 3));
	}
	else if (IS_MAP(value)) {
		series = VAL_SERIES(value);
		index = 0;
		//if (frame->tail > 3) Trap_Arg_DEAD_END(FRM_WORD(frame, 3));
	}
	else {
		series = VAL_SERIES(value);
		index  = VAL_INDEX(value);
		if (index >= cast(REBINT, SERIES_TAIL(series))) {
			if (mode == 1) {
				SET_INTEGER(D_OUT, 0);
			} else if (mode == 2) {
				Set_Block(D_OUT, out);
				UNSAVE_SERIES(out);
			}
			return R_OUT;
		}
	}

	windex = index;

	// Iterate over each value in the 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(value)) {
						*vars = *BLK_SKIP(series, index);
					}

					else if (ANY_OBJECT(value)) {
						if (!VAL_GET_EXT(BLK_SKIP(out, index), EXT_WORD_HIDE)) {
							// Alternate between word and value parts of object:
							if (j == 0) {
								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
								Trap_Arg_DEAD_END(words);
							j++;
						}
						else {
							// Do not evaluate this iteration
							index++;
							goto skip_hidden;
						}
					}

					else if (IS_VECTOR(value)) {
						Set_Vector_Value(vars, series, index);
					}

					else if (IS_MAP(value)) {
						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
								Trap_Arg_DEAD_END(words);
							j++;
						}
						else {
							index += 2;
							goto skip_hidden;
						}
					}

					else { // A string or binary
						if (IS_BINARY(value)) {
							SET_INTEGER(vars, (REBI64)(BIN_HEAD(series)[index]));
						}
						else if (IS_IMAGE(value)) {
							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(value) || IS_MAP(value)) {
					*vars = *value;
				} else {
					VAL_SET(vars, REB_BLOCK);
					VAL_SERIES(vars) = series;
					VAL_INDEX(vars) = index;
				}
				//if (index < tail) index++; // do not increment block.
			}
			else Trap_Arg_DEAD_END(words);
		}
		if (index == rindex) index++; //the word block has only set-words: foreach [a:] [1 2 3][]

		if (!DO_BLOCK(D_OUT, body, 0)) {
			if ((err = Check_Error(D_OUT)) >= 0) {
				index = rindex;
				break;
			}
			// else CONTINUE:
			if (mode == 1) SET_FALSE(D_OUT); // keep the value (for mode == 1)
		} else {
			err = 0; // prevent later test against uninitialized value
		}

		if (mode > 0) {
			//if (ANY_OBJECT(value)) Trap_Types_DEAD_END(words, REB_BLOCK, VAL_TYPE(value)); //check not needed

			// If FALSE return, copy values to the write location:
			if (mode == 1) {  // remove-each
				if (IS_CONDITIONAL_FALSE(D_OUT)) {
					REBCNT wide = SERIES_WIDE(series);
					// memory areas may overlap, so use memmove and not memcpy!
					memmove(series->data + (windex * wide), series->data + (rindex * wide), (index - rindex) * wide);
					windex += index - rindex;
					// old: while (rindex < index) *BLK_SKIP(series, windex++) = *BLK_SKIP(series, rindex++);
				}
			}
			else
				if (!IS_UNSET(D_OUT)) Append_Value(out, D_OUT); // (mode == 2)
		}
skip_hidden: ;
	}

	// Finish up:
	if (mode == 1) {
		// Remove hole (updates tail):
		if (windex < index) Remove_Series(series, windex, index - windex);
		SET_INTEGER(D_OUT, index - windex);

		return R_OUT;
	}

	// If MAP...
	if (mode == 2) {
		UNSAVE_SERIES(out);
		if (err != 2) {
			// ...and not BREAK/RETURN:
			Set_Block(D_OUT, out);
			return R_OUT;
		}
	}

	return R_OUT;
}