  Allow inline construction of anonymous arrays and "named arguments"

  Available as a patch:
  http://sourceforge.net/tracker/index.php?func=detail&aid=1592340&group_id=11005&atid=311005
	    [ 1592340 ] Array literals and named arguments
	    anonArrayNamedArgs3.diff 2006-11-16 02:34

  This patch allows arrays to be created in-line as anonymous entities. For
  example:
	    my_array = { \
	        1, 2, 3,                # assigns elements [0] to [2] \
	         ["word"] = "val",      # assigns element ["word"] \
	        ,                       # don't assign to [3] \
	        4,                      # assigns element [4] = 4 \
	        [7] = 7, 8,             # assigns elements [7] and [8] \
	        ["sub"] = { "a", "b" }  # assigns an array to element ["sub"] \
	    }
    The anonymous array is available in contexts where a whole array is needed;
    for example:
	    val0 = { [0] = "zero"}[0]
	    my_array += { [val0] = 4 + 5 - 9 }

    The patch also allows macro functions to be called with array element style
    assignments. These cannot use simple numeric or numeric string indices, but
    multidimensional numeric indices are allowed.

    The array element style ("named") arguments are inserted into the called
    routine's $args array, and accessible using array syntax. Simple argument
    expressions are counted from the first ($1), with numeric index 1 in $args,
    up to $n_args. Note that $args[] and $n_args may now be different since
    the former will include the count of "named" arguments.

    For example:
	    define returnArgs {
	        return $args
	    }
	    a = returnArgs(1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi")
	    b = { , 1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi" }
    Here the arrays a and b are equal. (Note the comma at the start of b's
    assigned array.)
    But:
	    a = returnArgs(1, [2]="no") # fails: name for arg cannot be numeric

    Finally, calling a built-in macro function always makes sure an extra
    argument is present at argList[nArgs] - in calls using positional values
    only, this argument will have a tag value of NO_TAG. If "named" arguments
    have been passed, the argument will have tag ARRAY_TAG, and the named
    arguments be present in the array, indexed by name. (The positional
    arguments will not be in this array but can be added to it readily.)
    Use the ArrayGet() function or iterate over entries as required.

diff -ur nedit_official nedit_mod
diff -ur nedit_official/doc/help.etx nedit_mod/doc/help.etx
--- nedit_official/doc/help.etx	2008-02-19 23:31:51.000000000 +0100
+++ nedit_mod/doc/help.etx	2008-03-17 23:49:57.000000000 +0100
@@ -2045,10 +2045,26 @@
 
      function_name(arg1, arg2, ...)
 
-  where arg1, arg2, etc. represent the argument values which are passed to
-  the routine being called.  A function or subroutine call can be on a line by
-  itself, as above, or if it returns a value, can be invoked within a character
-  or numeric expression:
+  where arg1, arg2, etc. represent the arguments which are passed to
+  the routine being called.  Arguments can be one of two kinds: positional or
+  named.  A positional argument is passed as an expression in the argument
+  list.  A named argument is passed by indicating the name as an array key,
+  between square brackets, followed by the "=" operator and an expression
+  for the argument's value.  Named arguments can use any valid array key value
+  as long as it cannot be converted into an integer value.
+
+  For example, the call
+
+     result = func(["prompt"] = "Available values are:", \
+                   a, b, c, d, e, f, \
+                   ["buttons"] = { "OK", "Change", "Cancel" })
+
+  provides six positional arguments (with the values of variables a, b, c, d, e
+  and f), and the named arguments "prompt", with a string value, and "buttons"
+  with an array value.
+
+  A function or subroutine call can be on a line by itself, as above, or if it
+  returns a value, can be invoked within a character or numeric expression:
 
      a = fn1(b, c) + fn2(d)
      dialog("fn3 says: " fn3())
@@ -2095,11 +2111,14 @@
   the autoload macro file, cf. Preferences_. Macro files can be loaded with
   File -> Load Macro File or with the load_macro_file() action.
 
-  The arguments with which a user-defined subroutine or function was invoked,
-  are presented as $1, $2, ... , $9 or $args[expr], where expr can be evaluated
-  to an integer from 1 to the number of arguments.  The number of arguments can
-  be read from $n_args or $args[]. The array $args[expr] is the only way to
-  access arguments beyond the first 9.
+  Within the body of a user-defined subroutine or function, the first nine
+  positional arguments can be retrieved using the identifiers $1, $2, ... , $9.
+  Both positional and named arguments can be accessed using the $args array:
+  if the key is numeric, the corresponding positional argument (numbered from 1)
+  can be retrieved; otherwise the key is a name, and the name argument's value
+  is retrieved.  The identifier $n_args provides the number of positional
+  arguments passed to the function.  You can test for the presence of named
+  arguments in the $args array using the "in" operator.
 
   To return a value from a subroutine, and/or to exit from the subroutine
   before the end of the subroutine body, use the return statement:
@@ -2238,7 +2257,7 @@
   All  of the above operators are array only, meaning both the left and right
   sides of the operator must be arrays. The results are also arrays.
 
-  Array keys can also contain multiple dimensions:
+  Array keys can contain multiple "dimensions":
 
       x[1, 1, 1] = "string"
 
@@ -2283,6 +2302,67 @@
 
   does work.
 
+  Note that if an array contains a value that is itself an array, you can
+  apply the index operator more than once. For example
+
+      subarray["a"] = "value"
+      mainarray[1] = subarray
+
+      for (i in mainarray) {
+        if ("a" in mainarray[i])
+          value_a = mainarray[i]["a"]
+        ...
+      }
+
+4>Array Initializing Expressions
+
+  You can build arrays using array expressions. These are contained in braces,
+  "{" and "}", and contain a list of possibly empty value assignments.
+  For example
+
+      myarray = { ["a"] = "first", \
+                  ["col" colno] = x * 5, \
+                  [x, y] = 2 * func() }
+
+  If the keys are numeric (or convertible to plain integers) and in increasing
+  sequence, only the first is required; thus
+
+      myarray = { [5] = a, b, c, \
+                  [1,2] = "2-D key", \
+                  ["20"] = d, e }
+
+  creates entries with keys "5", "6", "7", "20", "21" and ("1" $sub_sep "2").
+  If no key value is given for the first entry, "0" is used. If you want to skip
+  a value in a sequence, just provide an empty value, thus
+
+      myarray = { a, b, , c }
+
+  creates entries with keys "0", "1" and "3". The entry for key "2" is not
+  created.
+
+  If a later array entry has the same key value as an earlier one, the later
+  value overwrites the earlier one. For example
+
+      myarray = { a, b, c, [1] = d, e }
+
+  overwrites the myarray["1"] entry, initialized with the value of b, with the
+  value of d. Similarly the myarray["2"] entry is overwritten.
+
+  You can use array initializing expressions as part of other expressions. They
+  can be passed as arguments to functions:
+
+      result = func({ ["message"] = "The value is", \
+                      ["value"] = 4, "OK" })
+
+  Or you can use them to add to arrays, as in
+
+      myarray += { [newkey] = newvalue }
+
+  The built-in variable $empty_array evaluates to an empty array. You can also
+  build an empty array using array initializing expressions as follows:
+
+      myarray = {}
+
 3>Looping and Conditionals
 
   NEdit supports looping constructs: for and while, and conditional statements:
diff -ur nedit_official/source/interpret.c nedit_mod/source/interpret.c
--- nedit_official/source/interpret.c	2008-03-09 20:29:38.000000000 +0100
+++ nedit_mod/source/interpret.c	2008-03-18 00:02:39.000000000 +0100
@@ -118,6 +118,7 @@
 static int branch(void);
 static int branchTrue(void);
 static int branchFalse(void);
+static int branchIf(Boolean trueOrFalse);
 static int branchNever(void);
 static int arrayRef(void);
 static int arrayAssign(void);
@@ -126,6 +127,16 @@
 static int arrayIter(void);
 static int inArray(void);
 static int deleteArrayElement(void);
+static int anonArrayOpen(void);
+static int anonArraySkip(void);
+static int anonArrayNextVal(void);
+static int anonArrayIndexVal(void);
+static int anonArrayClose(void);
+static int namedArg1(void);
+static int namedArgN(void);
+static int namedArg1orN(Boolean isFirst);
+static int swapTop2(void);
+static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams);
 static void freeSymbolTable(Symbol *symTab);
 static int errCheck(const char *s);
 static int execError(const char *s1, const char *s2);
@@ -208,13 +219,16 @@
     assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse,
     branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
     deleteArrayElement, pushArraySymVal,
-    arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray};
+    arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray,
+    anonArrayOpen, anonArraySkip, anonArrayNextVal, anonArrayIndexVal,
+    anonArrayClose, namedArg1, namedArgN, swapTop2,
+    };
 
-/* 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)
+/* 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))
@@ -226,6 +240,13 @@
 #define FP_GET_SYM_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN))
 #define FP_GET_SYM_VAL(xFrameP,xSym) (FP_GET_SYM_N(xFrameP, xSym->value.val.n))
 
+#define PUSH_CHECK_TOO_MUCH(n) \
+    (StackP + (n) > &TheStack[STACK_SIZE])
+
+#define PUSH_CHECK(n) \
+    if (PUSH_CHECK_TOO_MUCH(n)) \
+        return execError(StackOverflowMsg, "");
+
 /*
 ** Initialize macro language global variables.  Must be called before
 ** any macros are even parsed, because the parser uses action routine
@@ -393,7 +414,7 @@
     do { register Inst t, *l = L, *h = H - 1; \
          while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
     /* double-reverse method: reverse elements of both parts then whole lot */
-    /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
+    /* eg abcdeABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> ABCDabcde */
     reverseCode(start, boundary);   /* 1 */
     reverseCode(boundary, end);     /* 2 */
     reverseCode(start, end);        /* 3 */
@@ -472,7 +493,8 @@
     static DataValue noValue = {NO_TAG, {0}};
     Symbol *s;
     int i;
-    
+    int haveNamedArgs;
+
     /* Create an execution context (a stack, a stack pointer, a frame pointer,
        and a program counter) which will retain the program state across
        preemption and resumption of execution */
@@ -484,22 +506,27 @@
     context->runWindow = window;
     context->focusWindow = window;
 
+    haveNamedArgs = (nArgs < 0);
+    if (haveNamedArgs)
+        nArgs = -nArgs;
+
     /* Push arguments and call information onto the stack */
     for (i=0; i<nArgs; i++)
-    	*(context->stackP++) = args[i];
+        *(context->stackP++) = args[i];
 
+    if (!haveNamedArgs)
+        *(context->stackP++) = noValue; /* cached arg array */
+    
     context->stackP->val.subr = NULL; /* return PC */
     context->stackP->tag = NO_TAG;
     context->stackP++;
     
     *(context->stackP++) = noValue; /* old FrameP */
     
-    context->stackP->tag = NO_TAG; /* nArgs */
-    context->stackP->val.n = nArgs;
+    context->stackP->tag = INT_TAG; /* nArgs */
+    context->stackP->val.n = nArgs - haveNamedArgs;
     context->stackP++;
     
-    *(context->stackP++) = noValue; /* cached arg array */
-    
     context->frameP = context->stackP;
     
     /* Initialize and make room on the stack for local variables */
@@ -572,12 +599,16 @@
 }
 
 /*
+** Set up a new stack frame, with no caller arguments, in the current context,
+** and set up execution for Program *prog.
+**
 ** If a macro is already executing, and requests that another macro be run,
 ** this can be called instead of ExecuteMacro to run it in the same context
 ** as if it were a subroutine.  This saves the caller from maintaining
 ** separate contexts, and serializes processing of the two macros without
 ** additional work.
 */
+/* TODO: this function should really return a status (if fails PUSH_CHECK) */
 void RunMacroAsSubrCall(Program *prog)
 {
     Symbol *s;
@@ -585,6 +616,10 @@
 
     /* See subroutine "callSubroutine" for a description of the stack frame
        for a subroutine call */
+    /* if (PUSH_CHECK_TOO_MUCH(4)) return MACRO_ERROR; */
+
+    *(StackP++) = noValue; /* cached arg array */
+    
     StackP->tag = NO_TAG;
     StackP->val.inst = PC; /* return PC */
     StackP++;
@@ -593,18 +628,18 @@
     StackP->val.dataval = FrameP; /* old FrameP */
     StackP++;
     
-    StackP->tag = NO_TAG; /* nArgs */
+    StackP->tag = INT_TAG; /* nArgs */
     StackP->val.n = 0;
     StackP++;
     
-    *(StackP++) = noValue; /* cached arg array */
-    
     FrameP = StackP;
     PC = prog->code;
     for (s = prog->localSymList; s != NULL; s = s->next) {
+        /* if (PUSH_CHECK_TOO_MUCH(1)) return MACRO_ERROR; */
 	FP_GET_SYM_VAL(FrameP, s) = noValue;
 	StackP++;
     }
+    /* return MACRO_DONE? MACRO_PREEMPT? MACRO_TIME_LIMIT? */
 }
 
 void FreeRestartData(RestartData *context)
@@ -1256,6 +1291,7 @@
 {
     int nArgs, argNum;
     DataValue argVal, *resultArray;
+    char intStr[TYPE_INT_STR_SIZE(argNum)];
 
     DISASM_RT(PC-1, 1);
     STACKDUMP(0, 3);
@@ -1265,14 +1301,15 @@
     if (resultArray->tag != ARRAY_TAG) {
         resultArray->tag = ARRAY_TAG;
         resultArray->val.arrayPtr = ArrayNew();
-
+    }
+    /* load arguments from positional arg list if not already done */
+    sprintf(intStr, "%d", argNum + 1);
+    if (nArgs && !ArrayGet(resultArray, intStr, &argVal)) {
         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)) {
-                return(execError("array insertion failure", NULL));
+                return(execError("argument array insertion failure", NULL));
             }
         }
     }
@@ -1328,6 +1365,274 @@
 }
 
 /*
+** create an anonymous array and next index number value (0) on the stack (for
+** array construction expressions)
+**
+** Before: Prog->  [next], ...
+**         TheStack-> next, ...
+** After:  Prog->  [next], ...
+**         TheStack-> [empty-array, 0], next, ...
+*/
+static int anonArrayOpen(void)
+{
+    DataValue dataVal;
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(0, 3);
+
+    /* make an empty array */
+    dataVal.tag = ARRAY_TAG;
+    dataVal.val.arrayPtr = ArrayNew();
+
+    /* push the default next index value first */
+    PUSH_INT(0)
+
+    /* and the empty array */
+    PUSH(dataVal)
+
+    return STAT_OK;
+}
+
+/*
+** cause the auto-incrementing next index number value to increase without
+** actually creating an entry in the anonymous array (for array construction
+** expressions)
+**
+** Before: Prog->  [next], ...
+**         TheStack-> [anon-array, next-index], next, ...
+** After:  Prog->  [next], ...
+**         TheStack-> [anon-array, next-index+1], next, ...
+*/
+static int anonArraySkip(void)
+{
+    DataValue anonArray;
+    int nextIndex;
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(2, 3);
+
+    POP(anonArray)
+    POP_INT(nextIndex)
+
+    /* we need to increment the index for next time */
+    ++nextIndex;
+
+    /* push the default next index value first, then the array */
+    PUSH_INT(nextIndex)
+    PUSH(anonArray)
+
+    return STAT_OK;
+}
+
+/*
+** add an entry to the anonymous array at the stack head, using the numeric
+** index just below that; restack the incremented index and anonymous array
+** (for array construction expressions)
+**
+** Before: Prog->  [next], ...
+**         TheStack-> [expr, anon-array, next-index], next, ...
+** After:  Prog->  [next], ...
+**         TheStack-> [anon-array, next-index+1], next, ...
+*/
+static int anonArrayNextVal(void)
+{
+    DataValue exprVal, anonArray;
+    int nextIndex;
+    char numString[TYPE_INT_STR_SIZE(int)];
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(3, 3);
+
+    POP(exprVal)
+    POP(anonArray)
+    POP_INT(nextIndex)
+
+    sprintf(numString, "%d", nextIndex);
+    if (!ArrayInsert(&anonArray, AllocStringCpy(numString), &exprVal)) {
+        return(execError("array insertion failure", NULL));
+    }
+
+    /* we need to increment the index for next time */
+    ++nextIndex;
+
+    /* push the default next index value first, then the array */
+    PUSH_INT(nextIndex)
+    PUSH(anonArray)
+
+    return STAT_OK;
+}
+
+/*
+**
+** Before: Prog->  [nDim], next, ...
+**         TheStack-> [expr, indnDim, ... ind1, anon-array, next-index], next, ...
+** After:  Prog->  nDim, [next], ...
+**         TheStack-> [anon-array, new-next-index], next, ...
+*/
+static int anonArrayIndexVal(void)
+{
+    int errNum;
+    char *keyString = NULL;
+    DataValue exprVal, anonArray;
+    int nextIndex, index;
+    int nDim;
+
+    nDim = PC->value;
+    PC++;
+
+    DISASM_RT(PC-2, 2);
+    STACKDUMP(nDim+3, 3);
+
+    POP(exprVal)
+
+    /* the next nDim stack entries form the index */
+    errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
+    if (errNum != STAT_OK) {
+        return errNum;
+    }
+
+    POP(anonArray)
+    POP_INT(nextIndex)
+
+    /* if our index is numeric (or can be converted to a number) we must
+       change the next index value */
+    if (nDim == 1 && StringToNum(keyString, &index)) {
+        nextIndex = index + 1;
+    }
+
+    if (!ArrayInsert(&anonArray, keyString, &exprVal)) {
+        return(execError("array insertion failure", NULL));
+    }
+
+    /* push the default next index value first, then the array */
+    PUSH_INT(nextIndex)
+    PUSH(anonArray)
+
+    return STAT_OK;
+}
+
+/*
+** finish building an anonymous array by removing the next index number value
+** from the stack (for array construction expressions)
+**
+** Before: Prog->  [next], ...
+**         TheStack-> [anon-array, next-index], next, ...
+** After:  Prog->  [next], ...
+**         TheStack-> [anon-array], next, ...
+*/
+static int anonArrayClose(void)
+{
+    DataValue anonArray;
+    DataValue next_index;
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(2, 3);
+
+    /* remove top two elements */
+    POP(anonArray)
+    POP(next_index)
+    /* put back the array content */
+    PUSH(anonArray)
+
+    return STAT_OK;
+}
+
+/*
+** create an $args array for the named arg with index of nDim elements, and
+** value expr; leave result on top of stack
+**
+** Before: Prog->  [nDim], next, ...
+**         TheStack-> [expr, indnDim, ... ind1], argN-arg1, next, ...
+** After:  Prog->  nDim, [next], ...
+**         TheStack-> args, argN-arg1, next, ...
+*/
+static int namedArg1(void)
+{
+    return namedArg1orN(True);
+}
+
+/*
+** add the named arg with index of nDim elements, and value expr to the $args
+** array at the top of the stack
+**
+** Before: Prog->  [nDim], next, ...
+**         TheStack-> [expr, indnDim, ... ind1, args], argN-arg1, next, ...
+** After:  Prog->  nDim, [next], ...
+**         TheStack-> [args], argN-arg1, next, ...
+*/
+static int namedArgN()
+{
+    return namedArg1orN(False);
+}
+
+/*
+** implementation for namedArg1(), namedArgN()
+*/
+static int namedArg1orN(Boolean isFirst)
+{
+    int errNum;
+    char *keyString = NULL;
+    DataValue exprVal, argsArray;
+    int nDim, index;
+
+    nDim = (PC++)->value;
+
+    DISASM_RT(PC-2, 2);
+    STACKDUMP(nDim + (isFirst ? 2 : 1), 3);
+
+    POP(exprVal)
+
+    /* the next nDim stack entries form the index */
+    errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
+    if (errNum != STAT_OK) {
+        return errNum;
+    }
+
+    /* if our index is numeric (or can be converted to a number) we must
+       change the next index value */
+    if (nDim == 1 && StringToNum(keyString, &index)) {
+        return execError("named argument name must not be numeric", NULL);
+    }
+
+    if (isFirst) {
+        /* make a new empty array */
+        argsArray.tag = ARRAY_TAG;
+        argsArray.val.arrayPtr = NULL;
+    }
+    else {
+        /* use the array at the top of the stack */
+        POP(argsArray)
+    }
+
+    if (!ArrayInsert(&argsArray, keyString, &exprVal)) {
+        return(execError("named argument insertion failure", NULL));
+    }
+
+    /* and (re)push the array */
+    PUSH(argsArray)
+
+    return STAT_OK;
+}
+
+/*
+** exchange top two values on the stack
+*/
+static int swapTop2(void)
+{
+    DataValue dv1, dv2;
+
+    DISASM_RT(PC-1, 1);
+    STACKDUMP(2, 3);
+
+    POP(dv1)
+    POP(dv2)
+    PUSH(dv1)
+    PUSH(dv2)
+
+    return STAT_OK;
+}
+
+/*
 ** assign top value to next symbol
 **
 ** Before: Prog->  [symbol], next, ...
@@ -1887,7 +2192,9 @@
 /*
 ** Call a subroutine or function (user defined or built-in).  Args are the
 ** subroutine's symbol, and the number of arguments which have been pushed
-** on the stack.
+** on the stack. If this value is less than zero, use the absolute value,
+** but note that the last one is already the $args array so don't set aside
+** space for that.
 **
 ** For a macro subroutine, the return address, frame pointer, number of
 ** arguments and space for local variables are added to the stack, and the
@@ -1895,11 +2202,11 @@
 ** arguments are popped off the stack, and the routine is just called.
 **
 ** Before: Prog->  [subrSym], nArgs, next, ...
-**         TheStack-> argN-arg1, next, ...
+**         TheStack-> 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)
 {
@@ -1908,14 +2215,18 @@
     static DataValue noValue = {NO_TAG, {0}};
     Program *prog;
     char *errMsg;
-    
+    int haveNamedArgs;
+
     sym = PC->sym;
     PC++;
     nArgs = PC->value;
     PC++;
-    
+
+    haveNamedArgs = (nArgs < 0);
+    nArgs = (haveNamedArgs) ? -nArgs - 1 : nArgs;
+
     DISASM_RT(PC-3, 3);
-    STACKDUMP(nArgs, 3);
+    STACKDUMP(nArgs + haveNamedArgs, 3);
 
     /*
     ** If the subroutine is built-in, call the built-in routine
@@ -1923,13 +2234,16 @@
     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;
-	if (!sym->value.val.subr(FocusWindow, StackP,
-	    	nArgs, &result, &errMsg))
+        /* NB nArgs < 0 implies presence of named args array in last position */
+        if (!sym->value.val.subr(FocusWindow, StackP, nArgs, &result, &errMsg))
 	    return execError(errMsg, sym->name);
     	if (PC->func == fetchRetVal) {
     	    if (result.tag == NO_TAG) {
@@ -1949,28 +2263,32 @@
     ** values which are already there.
     */
     if (sym->type == MACRO_FUNCTION_SYM) {
-    	StackP->tag = NO_TAG; /* return PC */
-    	StackP->val.inst = PC;
-    	StackP++;
-        
-    	StackP->tag = NO_TAG; /* old FrameP */
-    	StackP->val.dataval = FrameP;
-    	StackP++;
-        
-    	StackP->tag = NO_TAG; /* nArgs */
-    	StackP->val.n = nArgs;
-    	StackP++;
-        
-        *(StackP++) = noValue; /* cached arg array */
-        
-    	FrameP = StackP;
-    	prog = sym->value.val.prog;
-    	PC = prog->code;
-	for (s = prog->localSymList; s != NULL; s = s->next) {
-	    FP_GET_SYM_VAL(FrameP, s) = noValue;
-	    StackP++;
-	}
-   	return STAT_OK;
+        PUSH_CHECK(3 + !haveNamedArgs)
+
+        if (!haveNamedArgs)
+            *(StackP++) = noValue;  /* push dummy named arg array */
+
+        StackP->tag = NO_TAG; /* return PC */
+        StackP->val.inst = PC;
+        StackP++;
+
+        StackP->tag = NO_TAG; /* old FrameP */
+        StackP->val.dataval = FrameP;
+        StackP++;
+
+        StackP->tag = NO_TAG; /* nArgs */
+        StackP->val.n = nArgs;
+        StackP++;
+
+        FrameP = StackP;
+        prog = sym->value.val.prog;
+        PC = prog->code;
+        for (s = prog->localSymList; s != NULL; s = s->next) {
+            PUSH_CHECK(1)
+            FP_GET_SYM_VAL(FrameP, s) = noValue;
+            StackP++;
+        }
+        return STAT_OK;
     }
     
     /*
@@ -1982,7 +2300,13 @@
     	XKeyEvent key_event;
 	Display *disp;
 	Window win;
-    
+
+        if (haveNamedArgs) {
+            return execError(
+                    "%s action routine called with named argument array",
+                    sym->name);
+        }
+
 	/* Create a fake event with a timestamp suitable for actions which need
 	   timestamps, a marker to indicate that the call was from a macro
 	   (to stop shell commands from putting up their own separate banner) */
@@ -2041,7 +2365,7 @@
 /*
 ** Return from a subroutine call
 ** Before: Prog->  [next], ...
-**         TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
+**         TheStack-> retVal?, ...(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
 ** After:  Prog->  next, ..., (in caller)[FETCH_RET_VAL?], ...
 **         TheStack-> retVal?, next, ...
 */
@@ -2118,34 +2442,26 @@
 */
 static int branchTrue(void)
 {
-    int value;
-    Inst *addr;
-    
-    DISASM_RT(PC-1, 2);
-    STACKDUMP(1, 3);
-
-    POP_INT(value)
-    addr = PC + PC->value;
-    PC++;
-    
-    if (value)
-    	PC = addr;
-    return STAT_OK;
+    return branchIf(True);
 }
 static int branchFalse(void)
 {
+    return branchIf(False);
+}
+static int branchIf(Boolean trueOrFalse)
+{
     int value;
     Inst *addr;
-    
+
     DISASM_RT(PC-1, 2);
     STACKDUMP(1, 3);
 
     POP_INT(value)
     addr = PC + PC->value;
     PC++;
-    
-    if (!value)
-    	PC = addr;
+
+    if (!value == !trueOrFalse)
+        PC = addr;
     return STAT_OK;
 }
 
@@ -2956,7 +3272,15 @@
         "ARRAY_REF_ASSIGN_SETUP",       /* arrayRefAndAssignSetup */
         "PUSH_ARG",                     /* $arg[expr] */
         "PUSH_ARG_COUNT",               /* $arg[] */
-        "PUSH_ARG_ARRAY"                /* $arg */
+        "PUSH_ARG_ARRAY",               /* $arg */
+        "ARRAY_OPEN",                   /* anonArrayOpen: "{...}" */
+        "ARRAY_SKIP",                   /* anonArraySkip: "{ , ...}" */
+        "ARRAY_NEXT_VAL",               /* anonArrayNextVal: "{ expr }" */
+        "ARRAY_INDEX_VAL",              /* anonArrayIndexVal: "{ [i]=expr }"  */
+        "ARRAY_CLOSE",                  /* anonArrayClose: "{...}" */
+        "NAMED_ARG1",                   /* namedArg1: "fn([...]=..., ...)" */
+        "NAMED_ARGN",                   /* namedArgN: "fn(..., [...]=...)" */
+        "SWAP_TOP2",                    /* swapTop2: cf namedArgN */
     };
     int i, j;
     
@@ -2982,7 +3306,14 @@
                     ++i;
                 }
                 else if (j == OP_SUBR_CALL) {
-                    printf("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
+                    int args = (int)inst[i+2];
+                    printf("%s ", inst[i+1].sym->name);
+                    if (args < 0) {
+                        printf("%d+args[] (%d)", -args - 1, args);
+                    }
+                    else {
+                        printf("%d args", args);
+                    }
                     i += 2;
                 }
                 else if (j == OP_BEGIN_ARRAY_ITER) {
@@ -2996,8 +3327,12 @@
                             inst[i+3].value, &inst[i+3] + inst[i+3].value);
                     i += 3;
                 }
-                else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
-                            j == OP_ARRAY_ASSIGN) {
+                else if (j == OP_ARRAY_REF ||
+                         j == OP_ARRAY_DELETE ||
+                         j == OP_ARRAY_ASSIGN ||
+                         j == OP_ANONARRAY_INDEX_VAL ||
+                         j == OP_NAMED_ARG1 ||
+                         j == OP_NAMED_ARGN) {
                     printf("nDim=%d", inst[i+1].value);
                     ++i;
                 }
@@ -3027,7 +3362,7 @@
 #define STACK_DUMP_ARG_PREFIX "Arg"
 static void stackdump(int n, int extra)
 {
-    /* TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
+    /* TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
     int nArgs = FP_GET_ARG_COUNT(FrameP);
     int i, offset;
     char buffer[sizeof(STACK_DUMP_ARG_PREFIX) + TYPE_INT_STR_SIZE(int)];
@@ -3046,9 +3381,9 @@
         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_COUNT_INDEX:        pos = "NArgs";  break;  /* number of arguments */
             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	2008-03-09 20:29:38.000000000 +0100
+++ nedit_mod/source/interpret.h	2008-03-17 23:49:57.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
+
 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,11 @@
     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_ANONARRAY_OPEN, OP_ANONARRAY_SKIP, OP_ANONARRAY_NEXT_VAL,
+    OP_ANONARRAY_INDEX_VAL, OP_ANONARRAY_CLOSE,
+    OP_NAMED_ARG1, OP_NAMED_ARGN, OP_SWAP_TOP2,
+    N_OPS};
 
 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-03-17 23:49:57.000000000 +0100
@@ -62,7 +62,8 @@
 %token <sym> NUMBER STRING SYMBOL
 %token DELETE ARG_LOOKUP
 %token IF WHILE ELSE FOR BREAK CONTINUE RETURN
-%type <nArgs> arglist
+%type <nArgs> arrlist arrentry
+%type <nArgs> arglist fnarglist fnarg
 %type <inst> cond comastmts for while else and or arrayexpr
 %type <sym> evalsym
 
@@ -238,7 +239,7 @@
                 ADD_OP(OP_DECR);
                 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED($4);
             }
-            | SYMBOL '(' arglist ')' {
+            | SYMBOL '(' fnarglist ')' {
                 ADD_OP(OP_SUBR_CALL);
                 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
             }
@@ -283,6 +284,55 @@
                 $$ = $1 + 1;
             }
             ;
+fnarg:      expr {
+                $$ = 0;
+            }
+            | '[' arglist ']' '=' expr {
+                $$ = $2;    /* how many index elements to read? */
+            }
+            ;
+fnarglist:  /* nothing */ {
+                $$ = 0;
+            }
+            | fnarg {
+                if ($1 > 0) {
+                    /* named argument code already knows about index length (see
+                       rule for arg: above); it needs to be assembled into an
+                       array, which must be created. */
+                    ADD_OP(OP_NAMED_ARG1); ADD_IMMED($1);
+                    $$ = -1;    /* negative single arg for named arg array */
+                }
+                else {
+                    /* a normal positional argument - leave value on stack */
+                    $$ = 1;
+                }
+            }
+            | fnarglist ',' fnarg {
+                if ($3 > 0) {
+                    /* named arg: $3 == how many indices to process */
+                    if ($1 >= 0) {
+                        /* first named arg: create the array */
+                        ADD_OP(OP_NAMED_ARG1); ADD_IMMED($3);
+                        $$ = -($1 + 1);         /* make arg count negative */
+                    }
+                    else {
+                        /* another named arg: add to array */
+                        ADD_OP(OP_NAMED_ARGN); ADD_IMMED($3);
+                        $$ = $1;                /* no new positional args */
+                    }
+                }
+                else {
+                    /* positional arg */
+                    if ($1 < 0) {
+                        ADD_OP(OP_SWAP_TOP2);   /* keep arg array as last */
+                        $$ = $1 - 1;
+                    }
+                    else {
+                        $$ = $1 + 1;            /* no named args yet */
+                    }
+                }
+            }
+            ;
 expr:       numexpr %prec CONCAT
             | expr numexpr %prec CONCAT {
                 ADD_OP(OP_CONCAT);
@@ -306,6 +356,36 @@
                 $$ = GetPC();
             }
             ;
+arrconstr0: '{' {
+                /* create an empty array into which to add things */
+                ADD_OP(OP_ANONARRAY_OPEN);
+            }
+            ;
+arrconstr:  arrconstr0 arrlist '}' {
+                /* we're done: the array is complete */
+                ADD_OP(OP_ANONARRAY_CLOSE);
+            }
+            ;
+arrlist:                  arrentry { $$ = $1; }
+            | arrlist ',' { if ($1 > 0) ADD_OP(OP_ANONARRAY_SKIP); }
+                          arrentry { $$ = $4; }
+            ;
+arrentry:   /* nothing */ {
+                /* a missing entry will skip an index value */
+                $$ = 1;
+            }
+            | expr {
+                /* make a suitable index >= 0 and add expr there */
+                ADD_OP(OP_ANONARRAY_NEXT_VAL);
+                $$ = 0;
+            }
+            | '[' arglist ']' '=' expr {
+                /* build the index from arglistopt and add expr there */
+                ADD_OP(OP_ANONARRAY_INDEX_VAL);
+                ADD_IMMED($2);
+                $$ = 0;
+            }
+            ;
 numexpr:    NUMBER {
                 ADD_OP(OP_PUSH_SYM); ADD_SYM($1);
             }
@@ -315,18 +395,22 @@
             | SYMBOL {
                 ADD_OP(OP_PUSH_SYM); ADD_SYM($1);
             }
-            | SYMBOL '(' arglist ')' {
+            | SYMBOL '(' fnarglist ')' {
                 ADD_OP(OP_SUBR_CALL);
                 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
                 ADD_OP(OP_FETCH_RET_VAL);
             }
             | '(' expr ')'
+            /* this doesn't work for $args["string"]:
             | ARG_LOOKUP '[' numexpr ']' {
                ADD_OP(OP_PUSH_ARG);
             }
+            */
+            /* this doesn't work if $args contains non-argnum indices
             | ARG_LOOKUP '[' ']' {
                ADD_OP(OP_PUSH_ARG_COUNT);
             }
+            */
             | ARG_LOOKUP {
                ADD_OP(OP_PUSH_ARG_ARRAY);
             }
@@ -406,6 +490,7 @@
             | numexpr IN numexpr {
                 ADD_OP(OP_IN_ARRAY);
             }
+            | arrconstr
             ;
 while:  WHILE {
             $$ = GetPC(); StartLoopAddrList();
