Skip to content

Commit

Permalink
Make APPLY :DO [:func ...] work like DO :func ...
Browse files Browse the repository at this point in the history
This makes APPLY :DO consistent with DO, as a special case for DO only.
It should deal with what will be perceived as an R3 bug by many users.
See http://issue.cc/r3/1950 for the rationale behind this.

The transformation is equivalent to changing this:
apply :do [:any-func ...]
into this:
apply :any-func [...]
without calling DO at all.

Originally suggested by Ladislav: rebol#80
  • Loading branch information
BrianHawley committed Feb 16, 2013
1 parent 6a79a7b commit cd8250e
Showing 1 changed file with 31 additions and 5 deletions.
36 changes: 31 additions & 5 deletions src/core/c-do.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include <stdio.h>
#include "sys-state.h"

REBNATIVE(do); // Forward declaration for detection and special cases
#define IS_DO(v) (IS_NATIVE(v) && (VAL_FUNC_CODE(v) == &N_do))

enum Eval_Types {
ET_INVALID, // not valid to evaluate
ET_WORD,
Expand Down Expand Up @@ -1275,9 +1278,9 @@ x*/ static REBINT Do_Args_Light(REBVAL *func, REBVAL *path, REBSER *block, REBCN
**
***********************************************************************/
{
REBINT ftype = VAL_TYPE(func) - REB_NATIVE; // function type
REBSER *block = VAL_SERIES(args);
REBCNT index = VAL_INDEX(args);
REBCNT dsp;
REBCNT dsf;

REBSER *words;
Expand All @@ -1286,11 +1289,15 @@ x*/ static REBINT Do_Args_Light(REBVAL *func, REBVAL *path, REBSER *block, REBCN
REBINT start;
REBVAL *val;

dsp = DSP; // in case we have to reset it later

reapply: // Go back here to start over with a new func

if (index > SERIES_TAIL(block)) index = SERIES_TAIL(block);

// Push function frame:
dsf = Push_Func(0, block, index, 0, func);
func = DSF_FUNC(dsf); // for safety
func = DSF_FUNC(dsf); // set to non-volatile reference for safety

// Determine total number of args:
words = VAL_FUNC_WORDS(func);
Expand All @@ -1301,17 +1308,36 @@ x*/ static REBINT Do_Args_Light(REBVAL *func, REBVAL *path, REBSER *block, REBCN
if (reduce) {
// Reduce block contents to stack:
n = 0;
// Check for DO any-function
if (index < BLK_LEN(block)) {
index = Do_Next(block, index, 0);
val = DS_TOP;
if (IS_DO(func) && ANY_FUNC(val)) {
func = val; // apply this func directly (volatile reference!)
DSP = dsp; // reset the stack
goto reapply; // go back to the beginning
}
n++;
}
while (index < BLK_LEN(block)) {
index = Do_Next(block, index, 0);
n++;
}
if (n > len) DSP = start + len;
}
else {
// Get args block and check for DO any-function
n = BLK_LEN(block) - index;
val = BLK_SKIP(block, index);
if (n > 0 && IS_DO(func) && ANY_FUNC(val)) {
func = val; // apply this func directly
index++; // skip past the func value in the args
DSP = dsp; // reset the stack
goto reapply; // go back to the beginning
}
// Copy block contents to stack:
n = VAL_BLK_LEN(args);
if (len < n) n = len;
memcpy(&DS_Base[start], BLK_SKIP(block, index), n * sizeof(REBVAL));
memcpy(&DS_Base[start], val, n * sizeof(REBVAL));
DSP = start + n - 1;
}

Expand Down Expand Up @@ -1346,7 +1372,7 @@ x*/ static REBINT Do_Args_Light(REBVAL *func, REBVAL *path, REBSER *block, REBCN

// Evaluate the function:
DSF = dsf;
Func_Dispatch[ftype](func);
Func_Dispatch[VAL_TYPE(func) - REB_NATIVE](func);
DSP = dsf;
DSF = PRIOR_DSF(dsf);
}
Expand Down

0 comments on commit cd8250e

Please sign in to comment.