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; }
*/ REBVAL *Get_Var_Safe(REBVAL *word) /* ** Get the word, but check if it will be safe to modify. ** ***********************************************************************/ { REBINT index = VAL_WORD_INDEX(word); REBSER *frame = VAL_WORD_FRAME(word); REBINT dsf; if (!frame) Trap1(RE_NOT_DEFINED, word); if (index >= 0) { if (VAL_PROTECTED(FRM_WORDS(frame) + index)) Trap1(RE_LOCKED_WORD, word); return FRM_VALUES(frame) + index; } // A negative index indicates that the value is in a frame on // the data stack, so now we must find it by walking back the // stack looking for the function that the word is bound to. dsf = DSF; while (frame != VAL_WORD_FRAME(DSF_WORD(dsf))) { dsf = PRIOR_DSF(dsf); if (dsf <= 0) Trap1(RE_NOT_DEFINED, word); // change error !!! } // if (Trace_Level) Dump_Stack_Frame(dsf); return DSF_ARGS(dsf, -index); }
*/ void Set_Var(REBVAL *word, REBVAL *value) /* ** Set the word (variable) value. (Use macro when possible). ** ***********************************************************************/ { REBINT index = VAL_WORD_INDEX(word); REBINT dsf; REBSER *frm; if (!HAS_FRAME(word)) Trap1(RE_NOT_DEFINED, word); // ASSERT(index, RP_BAD_SET_INDEX); ASSERT(VAL_WORD_FRAME(word), RP_BAD_SET_CONTEXT); // Print("Set %s to %s [frame: %x idx: %d]", Get_Word_Name(word), Get_Type_Name(value), VAL_WORD_FRAME(word), VAL_WORD_INDEX(word)); if (index > 0) { frm = VAL_WORD_FRAME(word); if (VAL_PROTECTED(FRM_WORDS(frm)+index)) Trap1(RE_LOCKED_WORD, word); FRM_VALUES(frm)[index] = *value; return; } if (index == 0) Trap0(RE_SELF_PROTECTED); // Find relative value: dsf = DSF; while (VAL_WORD_FRAME(word) != VAL_WORD_FRAME(DSF_WORD(dsf))) { dsf = PRIOR_DSF(dsf); if (dsf <= 0) Trap1(RE_NOT_DEFINED, word); // change error !!! } *DSF_ARGS(dsf, -index) = *value; }
*/ void Set_Var(const REBVAL *word, const REBVAL *value) /* ** Set the word (variable) value. (Use macro when possible). ** ***********************************************************************/ { REBINT index = VAL_WORD_INDEX(word); struct Reb_Call *call; REBSER *frm; assert(!THROWN(value)); if (!HAS_FRAME(word)) raise Error_1(RE_NOT_DEFINED, word); assert(VAL_WORD_FRAME(word)); // Print("Set %s to %s [frame: %x idx: %d]", Get_Word_Name(word), Get_Type_Name(value), VAL_WORD_FRAME(word), VAL_WORD_INDEX(word)); if (index > 0) { frm = VAL_WORD_FRAME(word); if (VAL_GET_EXT(FRM_WORDS(frm) + index, EXT_WORD_LOCK)) raise Error_1(RE_LOCKED_WORD, word); FRM_VALUES(frm)[index] = *value; return; } if (index == 0) raise Error_0(RE_SELF_PROTECTED); // Find relative value: call = DSF; while (VAL_WORD_FRAME(word) != VAL_WORD_FRAME(DSF_LABEL(call))) { call = PRIOR_DSF(call); if (!call) raise Error_1(RE_NOT_DEFINED, word); // change error !!! } *DSF_ARG(call, -index) = *value; }
*/ 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; }
*/ void Collect_Object(REBSER *prior) /* ** Collect words from a prior object. ** ***********************************************************************/ { REBVAL *words; REBINT *binds = WORDS_HEAD(Bind_Table); // GC safe to do here REBINT n; words = FRM_WORDS(prior); COPY_VALUES(words, BLK_HEAD(BUF_WORDS), SERIES_TAIL(prior)); SERIES_TAIL(BUF_WORDS) = SERIES_TAIL(prior); for (n = 1, words++; NOT_END(words); words++) // skips first = SELF binds[VAL_WORD_CANON(words)] = n++; }
*/ void Protected(REBVAL *word) /* ** Throw an error if word is protected. ** ***********************************************************************/ { REBSER *frm; REBINT index = VAL_WORD_INDEX(word); if (index > 0) { frm = VAL_WORD_FRAME(word); if (VAL_PROTECTED(FRM_WORDS(frm)+index)) Trap1(RE_LOCKED_WORD, word); } else if (index == 0) Trap0(RE_SELF_PROTECTED); }
*/ void Bind_Values_Core(REBVAL value[], REBSER *frame, REBCNT mode) /* ** Bind words in an array of values terminated with REB_END ** to a specified frame. See warnings on the functions like ** Bind_Values_Deep() about not passing just a singular REBVAL. ** ** Different modes may be applied: ** ** BIND_ONLY - Only bind words found in the frame. ** BIND_ALL - Add words to the frame during the bind. ** BIND_SET - Add set-words to the frame during the bind. ** (note: word must not occur before the SET) ** BIND_DEEP - Recurse into sub-blocks. ** ** NOTE: BIND_SET must be used carefully, because it does not ** bind prior instances of the word before the set-word. That is ** to say that forward references are not allowed. ** ***********************************************************************/ { REBVAL *words; REBCNT index; REBINT *binds = WORDS_HEAD(Bind_Table); // GC safe to do here CHECK_MEMORY(4); CHECK_BIND_TABLE; // Note about optimization: it's not a big win to avoid the // binding table for short blocks (size < 4), because testing // every block for the rare case adds up. // Setup binding table for (index = 1; index < frame->tail; index++) { words = FRM_WORD(frame, index); if (!VAL_GET_OPT(words, EXT_WORD_HIDE)) binds[VAL_BIND_CANON(words)] = index; } Bind_Values_Inner_Loop(binds, &value[0], frame, mode); // Reset binding table: for (words = FRM_WORDS(frame) + 1; NOT_END(words); words++) binds[VAL_BIND_CANON(words)] = 0; CHECK_BIND_TABLE; }
*/ void Check_Frame(REBSER *frame) /* ***********************************************************************/ { REBINT n; REBVAL *values = FRM_VALUES(frame); REBVAL *words = FRM_WORDS(frame); REBINT tail = SERIES_TAIL(frame); for (n = 0; n < tail; n++, values++, words++) { if (IS_END(words) || IS_END(values)) { Debug_Fmt("** Early %s end at index: %d", IS_END(words) ? "words" : "values", n); } } if (NOT_END(words) || NOT_END(values)) Debug_Fmt("** Missing %s end at index: %d type: %d", NOT_END(words) ? "words" : "values", n, VAL_TYPE(words)); }
*/ void Collect_Object(REBSER *prior) /* ** Collect words from a prior object. ** ***********************************************************************/ { REBVAL *words = FRM_WORDS(prior); REBINT *binds = WORDS_HEAD(Bind_Table); REBINT n; // this is necessary for COPY_VALUES below // to not overwrite memory BUF_WORDS does not own RESIZE_SERIES(BUF_WORDS, SERIES_TAIL(prior)); COPY_VALUES(words, BLK_HEAD(BUF_WORDS), SERIES_TAIL(prior)); SERIES_TAIL(BUF_WORDS) = SERIES_TAIL(prior); for (n = 1, words++; NOT_END(words); words++) // skips first = SELF binds[VAL_WORD_CANON(words)] = n++; }
RL_API int RL_Set_Field(REBSER *obj, u32 word, RXIARG val, int type) /* ** Set a field (context variable) of an object. ** ** Returns: ** The type arg, or zero if word not found in object or if field is protected. ** Arguments: ** obj - object pointer (e.g. from RXA_OBJECT) ** word - global word identifier (integer) ** val - new value for field ** type - datatype of value */ { REBVAL value = {0}; if (!(word = Find_Word_Index(obj, word, FALSE))) return 0; if (VAL_PROTECTED(FRM_WORDS(obj)+word)) return 0; // Trap1(RE_LOCKED_WORD, word); RXI_To_Value(FRM_VALUES(obj)+word, val, type); return type; }
*/ REBSER *Make_Object_Block(REBSER *frame, REBINT mode) /* ** Return a block containing words, values, or set-word: value ** pairs for the given object. Note: words are bound to original ** object. ** ** Modes: ** 1 for word ** 2 for value ** 3 for words and values ** ***********************************************************************/ { REBVAL *words = FRM_WORDS(frame); REBVAL *values = FRM_VALUES(frame); REBSER *block; REBVAL *value; REBCNT n; n = (mode & 4) ? 0 : 1; block = Make_Block(SERIES_TAIL(frame) * (n + 1)); for (; n < SERIES_TAIL(frame); n++) { if (!VAL_GET_OPT(words+n, OPTS_HIDE)) { if (mode & 1) { value = Append_Value(block); if (mode & 2) { VAL_SET(value, REB_SET_WORD); VAL_SET_LINE(value); } else VAL_SET(value, REB_WORD); //VAL_TYPE(words+n)); VAL_WORD_SYM(value) = VAL_BIND_SYM(words+n); VAL_WORD_INDEX(value) = n; VAL_WORD_FRAME(value) = frame; } if (mode & 2) { Append_Val(block, values+n); } } } return block; }
*/ void Bind_Block(REBSER *frame, REBVAL *block, REBCNT mode) /* ** Bind the words of a block to a specified frame. ** Different modes may be applied: ** BIND_ONLY - Only bind words found in the frame. ** BIND_ALL - Add words to the frame during the bind. ** BIND_SET - Add set-words to the frame during the bind. ** (note: word must not occur before the SET) ** BIND_DEEP - Recurse into sub-blocks. ** ***********************************************************************/ { REBVAL *words; REBCNT index; REBINT *binds = WORDS_HEAD(Bind_Table); // GC safe to do here CHECK_MEMORY(4); CHECK_BIND_TABLE; // for (index = 0; index < Bind_Table->tail; index++) // if (binds[index] != 0) Crash(1333); // Note about optimization: it's not a big win to avoid the // binding table for short blocks (size < 4), because testing // every block for the rare case adds up. // Setup binding table: index = 1; for (index = 1; index < frame->tail; index++) { words = FRM_WORD(frame, index); if (!VAL_GET_OPT(words, OPTS_HIDE)) binds[VAL_BIND_CANON(words)] = index; } Bind_Block_Words(frame, block, mode); // Reset binding table: for (words = FRM_WORDS(frame)+1; NOT_END(words); words++) binds[VAL_BIND_CANON(words)] = 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; }
*/ void Collect_Object(REBSER *prior) /* ** Collect words from a prior object. ** ***********************************************************************/ { REBVAL *words = FRM_WORDS(prior); REBINT *binds = WORDS_HEAD(Bind_Table); REBINT n; // this is necessary for memcpy below to not overwrite memory // BUF_WORDS does not own RESIZE_SERIES(BUF_WORDS, SERIES_TAIL(prior)); // Word values can be copied just as bits (these are EXT_WORD_TYPED) memcpy(BLK_HEAD(BUF_WORDS), words, SERIES_TAIL(prior) * sizeof(REBVAL)); SERIES_TAIL(BUF_WORDS) = SERIES_TAIL(prior); for (n = 1, words++; NOT_END(words); words++) // skips first = SELF binds[VAL_WORD_CANON(words)] = n++; }
*/ 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; }
xx*/ void Dump_Frame(REBSER *frame, REBINT limit) /* ***********************************************************************/ { REBINT n; REBVAL *values = FRM_VALUES(frame); REBVAL *words = FRM_WORDS(frame); if (limit == -1 || limit > (REBINT)SERIES_TAIL(frame)) limit = SERIES_TAIL(frame); Debug_Fmt("Frame: %x len = %d", frame, SERIES_TAIL(frame)); for (n = 0; n < limit; n++, values++, words++) { Debug_Fmt(" %02d: %s (%s) [%s]", n, Get_Sym_Name(VAL_BIND_SYM(words)), Get_Sym_Name(VAL_BIND_CANON(words)), Get_Type_Name(values) ); } if (limit >= (REBINT)SERIES_TAIL(frame) && NOT_END(words)) Debug_Fmt("** Word list not terminated! Type: %d, Tail: %d", VAL_TYPE(words), SERIES_TAIL(frame)); }
*/ 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 }
*/ REBVAL *Get_Var_Core(const REBVAL *word, REBOOL trap, REBOOL writable) /* ** Get the word--variable--value. (Generally, use the macros like ** GET_VAR or GET_MUTABLE_VAR instead of this). This routine is ** called quite a lot and so attention to performance is important. ** ** Coded assuming most common case is trap=TRUE and writable=FALSE ** ***********************************************************************/ { REBSER *context = VAL_WORD_FRAME(word); if (context) { REBINT index = VAL_WORD_INDEX(word); // POSITIVE INDEX: The word is bound directly to a value inside // a frame, and represents the zero-based offset into that series. // This is how values would be picked out of object-like things... // (Including looking up 'append' in the user context.) if (index > 0) { REBVAL *value; if ( writable && VAL_GET_EXT(FRM_WORDS(context) + index, EXT_WORD_LOCK) ) { if (trap) raise Error_1(RE_LOCKED_WORD, word); return NULL; } value = FRM_VALUES(context) + index; assert(!THROWN(value)); return value; } // NEGATIVE INDEX: Word is stack-relative bound to a function with // no persistent frame held by the GC. The value *might* be found // on the stack (or not, if all instances of the function on the // call stack have finished executing). We walk backward in the call // stack to see if we can find the function's "identifying series" // in a call frame...and take the first instance we see (even if // multiple invocations are on the stack, most recent wins) if (index < 0) { struct Reb_Call *call = DSF; // Get_Var could theoretically be called with no evaluation on // the stack, so check for no DSF first... while (call) { if ( call->args_ready && context == VAL_FUNC_WORDS(DSF_FUNC(call)) ) { REBVAL *value; assert(!IS_CLOSURE(DSF_FUNC(call))); if ( writable && VAL_GET_EXT( VAL_FUNC_PARAM(DSF_FUNC(call), -index), EXT_WORD_LOCK ) ) { if (trap) raise Error_1(RE_LOCKED_WORD, word); return NULL; } value = DSF_ARG(call, -index); assert(!THROWN(value)); return value; } call = PRIOR_DSF(call); } if (trap) raise Error_1(RE_NO_RELATIVE, word); return NULL; } // ZERO INDEX: The word is SELF. Although the information needed // to produce an OBJECT!-style REBVAL lives in the zero offset // of the frame, it's not a value that we can return a direct // pointer to. Use GET_VAR_INTO instead for that. assert(!IS_SELFLESS(context)); if (trap) raise Error_0(RE_SELF_PROTECTED); return NULL; // is this a case where we should *always* trap? } if (trap) raise Error_1(RE_NOT_DEFINED, word); return NULL; }
*/ 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); } }