    Allow an array to take the place of function arguments in a call

    The macro syntax is:
	        func( =array )

    Exactly one array argument is allowed. The content of this array is made
    available to the called function as $args. The positional arguments, $1 and
    so on, are copied from the array to the stack, using the corresponding
    numeric indices, increasing from 1 until there is a break in the sequence.

    2008-01-31

    Corrected this patch: resolving inconsistent stacking between ExecuteMacro()
    or RunMacroAsSubrCall() and callSubroutineFromSymbol(). Things would work
    unless you had executable statements in a macro file.
    
    Another correction handles how the argument array is passed when using the
    new syntax. Firstly, a copy is passed to the called function (before this
    would be the actual array); then the unnamed (numbered) arguments are
    removed from this copy as they are loaded onto the stack. If the called
    function then accesses "$args" the arguments are copied back into the array
    copy. This is to allow for further functions which reoder arguments such as
    call(function_name,arg1,arg2,...) to work when invoked with the new syntax.

diff -ur nedit_official nedit_mod
diff -ur nedit_official/source/interpret.c nedit_mod/source/interpret.c
--- nedit_official/source/interpret.c	2007-10-18 15:26:43.000000000 +0200
+++ nedit_mod/source/interpret.c	2008-01-31 01:13:26.000000000 +0100
@@ -113,7 +113,10 @@
 static int power(void);
 static int concat(void);
 static int assign(void);
+static int callSubroutineFromSymbol(Symbol *sym, int nArgs);
 static int callSubroutine(void);
+static int callSubroutineStackedN(void);
+static int unpackArrayToArgs(void);
 static int fetchRetVal(void);
 static int branch(void);
 static int branchTrue(void);
@@ -208,13 +211,16 @@
     assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse,
     branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
     deleteArrayElement, pushArraySymVal,
-    arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray};
-
-/* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
-#define FP_ARG_ARRAY_CACHE_INDEX (-1)
-#define FP_ARG_COUNT_INDEX (-2)
-#define FP_OLD_FP_INDEX (-3)
-#define FP_RET_PC_INDEX (-4)
+    arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray,
+    callSubroutineStackedN,
+    unpackArrayToArgs,
+};
+
+/* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
+#define FP_ARG_COUNT_INDEX (-1)
+#define FP_OLD_FP_INDEX (-2)
+#define FP_RET_PC_INDEX (-3)
+#define FP_ARG_ARRAY_CACHE_INDEX (-4)
 #define FP_TO_ARGS_DIST (4) /* should be 0 - (above index) */
 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
 #define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_CACHE_INDEX))
@@ -488,6 +494,8 @@
     for (i=0; i<nArgs; i++)
     	*(context->stackP++) = args[i];
 
+    *(context->stackP++) = noValue; /* cached arg array */
+    
     context->stackP->val.subr = NULL; /* return PC */
     context->stackP->tag = NO_TAG;
     context->stackP++;
@@ -498,8 +506,6 @@
     context->stackP->val.n = nArgs;
     context->stackP++;
     
-    *(context->stackP++) = noValue; /* cached arg array */
-    
     context->frameP = context->stackP;
     
     /* Initialize and make room on the stack for local variables */
@@ -585,6 +591,8 @@
 
     /* See subroutine "callSubroutine" for a description of the stack frame
        for a subroutine call */
+    *(StackP++) = noValue; /* cached arg array */
+    
     StackP->tag = NO_TAG;
     StackP->val.inst = PC; /* return PC */
     StackP++;
@@ -597,8 +605,6 @@
     StackP->val.n = 0;
     StackP++;
     
-    *(StackP++) = noValue; /* cached arg array */
-    
     FrameP = StackP;
     PC = prog->code;
     for (s = prog->localSymList; s != NULL; s = s->next) {
@@ -1222,28 +1228,32 @@
 static int pushArgArray(void)
 {
     int nArgs, argNum;
-    DataValue argVal, *resultArray;
+    DataValue argVal, *argArray;
+    char intStr[TYPE_INT_STR_SIZE(argNum)];
+    Boolean needArgCopy = False;
 
     DISASM_RT(PC-1, 1);
     STACKDUMP(0, 3);
 
     nArgs = FP_GET_ARG_COUNT(FrameP);
-    resultArray = &FP_GET_ARG_ARRAY_CACHE(FrameP);
-    if (resultArray->tag != ARRAY_TAG) {
-        resultArray->tag = ARRAY_TAG;
-        resultArray->val.arrayPtr = ArrayNew();
-
+    argArray = &FP_GET_ARG_ARRAY_CACHE(FrameP);
+    if (argArray->tag != ARRAY_TAG) {
+        /* we require a real array in the argArray position */
+        argArray->tag = ARRAY_TAG;
+        argArray->val.arrayPtr = ArrayNew();
+        needArgCopy = True;
+    }
+    if (needArgCopy || (nArgs > 0 && !ArrayGet(argArray,(char*)"1",&argVal))) {
+        /* we need to copy positional arguments into the argument array */
         for (argNum = 0; argNum < nArgs; ++argNum) {
-            char intStr[TYPE_INT_STR_SIZE(argNum)];
-
             sprintf(intStr, "%d", argNum + 1);
             argVal = FP_GET_ARG_N(FrameP, argNum);
-            if (!ArrayInsert(resultArray, AllocStringCpy(intStr), &argVal)) {
+            if (!ArrayInsert(argArray, AllocStringCpy(intStr), &argVal)) {
                 return(execError("array insertion failure", NULL));
             }
         }
     }
-    PUSH(*resultArray);
+    PUSH(*argArray);
     return STAT_OK;
 }
 
@@ -1863,29 +1873,32 @@
 ** arguments and space for local variables are added to the stack, and the
 ** PC is set to point to the new function. For a built-in routine, the
 ** arguments are popped off the stack, and the routine is just called.
-**
+*/
+/*
+** For callSubroutine:
 ** Before: Prog->  [subrSym], nArgs, next, ...
-**         TheStack-> argN-arg1, next, ...
+**         TheStack-> argArray?, argN-arg1, next, ...
+**
+** For callSubroutineStackedN:
+** Before: Prog->  [subrSym], next, ...
+**         TheStack-> nArgs, argArray, argN-arg1, next, ...
+**
 ** After:  Prog->  next, ...            -- (built-in called subr)
 **         TheStack-> retVal?, next, ...
 **    or:  Prog->  (in called)next, ... -- (macro code called subr)
-**         TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
+**         TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
 */
-static int callSubroutine(void)
+static int callSubroutineFromSymbol(Symbol *sym, int nArgs)
 {
-    Symbol *sym, *s;
-    int i, nArgs;
+    Symbol *s;
+    int i;
     static DataValue noValue = {NO_TAG, {0}};
     Program *prog;
     char *errMsg;
-    
-    sym = PC->sym;
-    PC++;
-    nArgs = PC->value;
-    PC++;
-    
-    DISASM_RT(PC-3, 3);
-    STACKDUMP(nArgs, 3);
+    int haveNamedArgs = (nArgs < 0);
+
+    if (haveNamedArgs)
+      nArgs = -nArgs - 1;
 
     /*
     ** If the subroutine is built-in, call the built-in routine
@@ -1893,8 +1906,12 @@
     if (sym->type == C_FUNCTION_SYM) {
     	DataValue result;
 
+        if (!haveNamedArgs) {
+            PUSH(noValue)       /* push dummy named arg array */
+        }
+
         /* "pop" stack back to the first argument in the call stack */
-    	StackP -= nArgs;
+        StackP -= nArgs + 1;
 
     	/* Call the function and check for preemption */
     	PreemptRequest = False;
@@ -1919,6 +1936,10 @@
     ** values which are already there.
     */
     if (sym->type == MACRO_FUNCTION_SYM) {
+
+        if (!haveNamedArgs)
+            *(StackP++) = noValue;  /* push dummy named arg array */
+
     	StackP->tag = NO_TAG; /* return PC */
     	StackP->val.inst = PC;
     	StackP++;
@@ -1930,9 +1951,7 @@
     	StackP->tag = NO_TAG; /* nArgs */
     	StackP->val.n = nArgs;
     	StackP++;
-        
-        *(StackP++) = noValue; /* cached arg array */
-        
+
     	FrameP = StackP;
     	prog = (Program *)sym->value.val.str.rep;
     	PC = prog->code;
@@ -1989,6 +2008,117 @@
 }
 
 /*
+** Before: Prog->  [subrSym], nArgs, next, ...
+**         TheStack-> argN-arg1, next, ...
+**
+** After:  Prog->  next, ...            -- (built-in called subr)
+**         TheStack-> retVal?, next, ...
+**    or:  Prog->  (in called)next, ... -- (macro code called subr)
+**         TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
+*/
+static int callSubroutine(void)
+{
+    Symbol *sym;
+    int nArgs;
+    int n;
+
+    sym = PC++->sym;
+    nArgs = PC++->value;
+
+    DISASM_RT(PC-3, 3);
+    STACKDUMP(nArgs > 0 ? nArgs : -nArgs, 3);
+
+    return callSubroutineFromSymbol(sym, nArgs);
+}
+
+/*
+** Before: Prog->  [subrSym], next, ...
+**         TheStack-> nArgs, argArray, argN-arg1, next, ...
+**
+** After:  Prog->  next, ...            -- (built-in called subr)
+**         TheStack-> retVal?, next, ...
+**    or:  Prog->  (in called)next, ... -- (macro code called subr)
+**         TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
+*/
+static int callSubroutineStackedN(void)
+{
+    Symbol *sym;
+    int nArgs;
+    /* this is much like callSubroutine, but we get nArgs off the stack
+       and it will always be negative since there is always an argArray */
+
+    sym = PC++->sym;
+
+    PEEK_INT(nArgs, 0)
+    DISASM_RT(PC-2, 2);
+    STACKDUMP(-nArgs + 1, 3);   /* +1 for stacked nArgs */
+
+    POP_INT(nArgs)
+
+    if (nArgs >= 0) {
+        /* should never happen */
+        return execError("array argument call to %s erroneous", sym->name);
+    }
+
+    return callSubroutineFromSymbol(sym, nArgs);
+}
+
+/*
+** For special call style where the $args array in the called function is
+** assigned from an array in the caller (as "calledFunc(=argsArray)"),
+** take consecutive elements indexed from 1 and put them on the stack, leaving
+** a copy of the actual array at the top of the stack, with the stacked
+** arguments removed. Finally, add the negative of the number of arguments
+** aplus 1 (for the argArray itself). This operation must be followed
+** by OP_SUBR_CALL_STACKED_N (callSubroutineStackedN()).
+**
+** The array copy is needed because if/when the $args array is accessed, the
+** arguments are copied back to the array, probably in different positions, as
+** is the case of a "call(=array)" function, where the first argument is
+** removed (the function name) and the others shifted down once. Without a
+** copy, this modifies the original array - a pass by reference not allowed in
+** the language.
+**
+** Before: Prog->  next, ...
+**         TheStack-> argArray, next, ...
+** After:  Prog->  next, ...
+**         TheStack-> -(nArgs+1), argArray, argN-arg1, next, ...
+*/
+static int unpackArrayToArgs(void)
+{
+    int nArgs, res;
+
+    DataValue dvEntry, dvArray;
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(1, 3);
+
+    POP(dvEntry)
+
+    if (dvEntry.tag != ARRAY_TAG) {
+        return execError("argument array call made with non-array value", NULL);
+    }
+    res = ArrayCopy(&dvArray, &dvEntry);
+    if (res != STAT_OK) {
+        return execError("cannot copy array in array call", NULL);
+    }
+
+    /* push positional argument entries in the array on the stack */
+    for (nArgs = 1; ; ++nArgs) {
+        char ind[TYPE_INT_STR_SIZE(nArgs) + 1];
+        sprintf(ind, "%d", nArgs);
+        if (!ArrayGet(&dvArray, ind, &dvEntry))
+            break;
+        /* remove them from remaining array */
+        ArrayDelete(&dvArray, ind);
+        PUSH(dvEntry)
+    }
+    PUSH(dvArray)
+    PUSH_INT(-nArgs)
+    return STAT_OK;
+}
+
+/*
 ** This should never be executed, returnVal checks for the presence of this
 ** instruction at the PC to decide whether to push the function's return
 ** value, then skips over it without executing.
@@ -2926,7 +3056,9 @@
         "ARRAY_REF_ASSIGN_SETUP",       /* arrayRefAndAssignSetup */
         "PUSH_ARG",                     /* $arg[expr] */
         "PUSH_ARG_COUNT",               /* $arg[] */
-        "PUSH_ARG_ARRAY"                /* $arg */
+        "PUSH_ARG_ARRAY",               /* $arg */
+        "SUBR_CALL_STACKED_N",          /* callSubroutineStackedN */
+        "UNPACKTOARGS",                 /* unpackArrayToArgs */
     };
     int i, j;
     
@@ -2955,6 +3087,10 @@
                     printf("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
                     i += 2;
                 }
+                else if (j == OP_SUBR_CALL_STACKED_N) {
+                    printf("%s args[] (?)", inst[i+1].sym->name);
+                    ++i;
+                }
                 else if (j == OP_BEGIN_ARRAY_ITER) {
                     printf("%s in", inst[i+1].sym->name);
                     ++i;
@@ -3015,10 +3151,10 @@
         printf("%8p ", dv);
         switch (offset) {
             case 0:                         pos = "FrameP"; break;  /* first local symbol value */
-            case FP_ARG_ARRAY_CACHE_INDEX:  pos = "args";   break;  /* arguments array */
             case FP_ARG_COUNT_INDEX:        pos = "NArgs";  break;  /* number of arguments */
             case FP_OLD_FP_INDEX:           pos = "OldFP";  break;
             case FP_RET_PC_INDEX:           pos = "RetPC";  break;
+            case FP_ARG_ARRAY_CACHE_INDEX:  pos = "args";   break;  /* arguments array */
             default:
                 if (offset < -FP_TO_ARGS_DIST && offset >= -FP_TO_ARGS_DIST - nArgs) {
                     sprintf(pos = buffer, STACK_DUMP_ARG_PREFIX "%d",
diff -ur nedit_official/source/interpret.h nedit_mod/source/interpret.h
--- nedit_official/source/interpret.h	2007-01-12 17:17:42.000000000 +0100
+++ nedit_mod/source/interpret.h	2008-01-31 00:30:28.000000000 +0100
@@ -40,7 +40,7 @@
 
 enum symTypes {CONST_SYM, GLOBAL_SYM, LOCAL_SYM, ARG_SYM, PROC_VALUE_SYM,
     	C_FUNCTION_SYM, MACRO_FUNCTION_SYM, ACTION_ROUTINE_SYM};
-#define N_OPS 43
+#define N_OPS 45
 enum operations {OP_RETURN_NO_VAL, OP_RETURN, OP_PUSH_SYM, OP_DUP, OP_ADD,
     OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_NEGATE, OP_INCR, OP_DECR, OP_GT, OP_LT,
     OP_GE, OP_LE, OP_EQ, OP_NE, OP_BIT_AND, OP_BIT_OR, OP_AND, OP_OR, OP_NOT,
@@ -48,7 +48,10 @@
     OP_BRANCH_TRUE, OP_BRANCH_FALSE, OP_BRANCH_NEVER, OP_ARRAY_REF,
     OP_ARRAY_ASSIGN, OP_BEGIN_ARRAY_ITER, OP_ARRAY_ITER, OP_IN_ARRAY,
     OP_ARRAY_DELETE, OP_PUSH_ARRAY_SYM, OP_ARRAY_REF_ASSIGN_SETUP, OP_PUSH_ARG,
-    OP_PUSH_ARG_COUNT, OP_PUSH_ARG_ARRAY};
+    OP_PUSH_ARG_COUNT, OP_PUSH_ARG_ARRAY,
+    OP_SUBR_CALL_STACKED_N,
+    OP_UNPACKTOARGS,
+    };
 
 enum typeTags {NO_TAG, INT_TAG, STRING_TAG, ARRAY_TAG};
 
diff -ur nedit_official/source/parse.y nedit_mod/source/parse.y
--- nedit_official/source/parse.y	2007-01-12 17:17:42.000000000 +0100
+++ nedit_mod/source/parse.y	2008-01-31 00:30:28.000000000 +0100
@@ -238,10 +238,7 @@
                 ADD_OP(OP_DECR);
                 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED($4);
             }
-            | SYMBOL '(' arglist ')' {
-                ADD_OP(OP_SUBR_CALL);
-                ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
-            }
+            | funccall
             | INCR SYMBOL {
                 ADD_OP(OP_PUSH_SYM); ADD_SYM($2); ADD_OP(OP_INCR);
                 ADD_OP(OP_ASSIGN); ADD_SYM($2);
@@ -283,6 +280,20 @@
                 $$ = $1 + 1;
             }
             ;
+
+/* function call and its argument lists */
+funccall:     SYMBOL '(' arglist ')' {
+                ADD_OP(OP_SUBR_CALL);
+                ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
+            }
+            | SYMBOL '(' '=' expr ')' {
+                /* a single array replaces the argument list */
+                ADD_OP(OP_UNPACKTOARGS);
+                ADD_OP(OP_SUBR_CALL_STACKED_N);
+                ADD_SYM(PromoteToGlobal($1));
+            }
+            ;
+
 expr:       numexpr %prec CONCAT
             | expr numexpr %prec CONCAT {
                 ADD_OP(OP_CONCAT);
@@ -315,9 +326,7 @@
             | SYMBOL {
                 ADD_OP(OP_PUSH_SYM); ADD_SYM($1);
             }
-            | SYMBOL '(' arglist ')' {
-                ADD_OP(OP_SUBR_CALL);
-                ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
+            | funccall {
                 ADD_OP(OP_FETCH_RET_VAL);
             }
             | '(' expr ')'
