Update.
[nedit.git] / source / interpret.c
blobb3fef85146c60170c86e6ad85197409c82d1fd72
1 static const char CVSID[] = "$Id: interpret.c,v 1.32 2002/12/12 17:25:49 slobasso Exp $";
2 /*******************************************************************************
3 * *
4 * interpret.c -- Nirvana Editor macro interpreter *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * April, 1997 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "interpret.h"
34 #include "textBuf.h"
35 #include "nedit.h"
36 #include "menu.h"
37 #include "text.h"
38 #include "rbTree.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <math.h>
44 #include <limits.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #ifdef VMS
48 #include "../util/VMSparam.h"
49 #else
50 #ifndef __MVS__
51 #include <sys/param.h>
52 #endif
53 #endif /*VMS*/
55 #include <X11/Intrinsic.h>
56 #include <Xm/Xm.h>
58 #ifdef HAVE_DEBUG_H
59 #include "../debug.h"
60 #endif
62 #define PROGRAM_SIZE 4096 /* Maximum program size */
63 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
64 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
65 allowed per program */
66 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
67 allowed to execute before preempting and
68 returning to allow other things to run */
70 /* Temporary markers placed in a branch address location to designate
71 which loop address (break or continue) the location needs */
72 #define NEEDS_BREAK (Inst)1
73 #define NEEDS_CONTINUE (Inst)2
75 #define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */
77 enum opStatusCodes {STAT_OK=2, STAT_DONE, STAT_ERROR, STAT_PREEMPT};
79 static void addLoopAddr(Inst *addr);
80 static void saveContext(RestartData *context);
81 static void restoreContext(RestartData *context);
82 static int returnNoVal(void);
83 static int returnVal(void);
84 static int returnValOrNone(int valOnStack);
85 static int pushSymVal(void);
86 static int pushArraySymVal(void);
87 static int dupStack(void);
88 static int add(void);
89 static int subtract(void);
90 static int multiply(void);
91 static int divide(void);
92 static int modulo(void);
93 static int negate(void);
94 static int increment(void);
95 static int decrement(void);
96 static int gt(void);
97 static int lt(void);
98 static int ge(void);
99 static int le(void);
100 static int eq(void);
101 static int ne(void);
102 static int bitAnd(void);
103 static int bitOr(void);
104 static int and(void);
105 static int or(void);
106 static int not(void);
107 static int power(void);
108 static int concat(void);
109 static int assign(void);
110 static int callSubroutine(void);
111 static int fetchRetVal(void);
112 static int branch(void);
113 static int branchTrue(void);
114 static int branchFalse(void);
115 static int branchNever(void);
116 static int arrayRef(void);
117 static int arrayAssign(void);
118 static int arrayRefAndAssignSetup(void);
119 static int beginArrayIter(void);
120 static int arrayIter(void);
121 static int inArray(void);
122 static int deleteArrayElement(void);
123 static void freeSymbolTable(Symbol *symTab);
124 static int errCheck(const char *s);
125 static int execError(const char *s1, const char *s2);
126 static rbTreeNode *arrayEmptyAllocator(void);
127 static rbTreeNode *arrayAllocateNode(rbTreeNode *src);
128 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src);
129 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right);
130 static void arrayDisposeNode(rbTreeNode *src);
131 static SparseArrayEntry *allocateSparseArrayEntry(void);
133 /*#define DEBUG_ASSEMBLY*/
134 /*#define DEBUG_STACK*/
136 #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK)
137 #define DEBUG_DISASSEMBLER
138 static void disasm(Inst *inst, int nInstr);
139 #endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */
141 #ifdef DEBUG_ASSEMBLY /* for disassembly */
142 #define DISASM(i, n) disasm(i, n)
143 #else /* #ifndef DEBUG_ASSEMBLY */
144 #define DISASM(i, n)
145 #endif /* #ifndef DEBUG_ASSEMBLY */
147 #ifdef DEBUG_STACK /* for run-time instruction and stack trace */
148 static void stackdump(int n, int extra);
149 #define STACKDUMP(n, x) stackdump(n, x)
150 #define DISASM_RT(i, n) disasm(i, n)
151 #else /* #ifndef DEBUG_STACK */
152 #define STACKDUMP(n, x)
153 #define DISASM_RT(i, n)
154 #endif /* #ifndef DEBUG_STACK */
156 /* Global symbols and function definitions */
157 static Symbol *GlobalSymList = NULL;
159 /* List of all memory allocated for strings */
160 static char *AllocatedStrings = NULL;
162 typedef struct {
163 SparseArrayEntry data; /* LEAVE this as top entry */
164 int inUse; /* we use pointers to the data to refer to the entire struct */
165 struct SparseArrayEntryWrapper *next;
166 } SparseArrayEntryWrapper;
168 static SparseArrayEntryWrapper *AllocatedSparseArrayEntries = NULL;
170 /* Message strings used in macros (so they don't get repeated every time
171 the macros are used */
172 static const char *StackOverflowMsg = "macro stack overflow";
173 static const char *StackUnderflowMsg = "macro stack underflow";
174 static const char *StringToNumberMsg = "string could not be converted to number";
176 /* Temporary global data for use while accumulating programs */
177 static Symbol *LocalSymList = NULL; /* symbols local to the program */
178 static Inst Prog[PROGRAM_SIZE]; /* the program */
179 static Inst *ProgP; /* next free spot for code gen. */
180 static Inst *LoopStack[LOOP_STACK_SIZE]; /* addresses of break, cont stmts */
181 static Inst **LoopStackPtr = LoopStack; /* to fill at the end of a loop */
183 /* Global data for the interpreter */
184 static DataValue *Stack; /* the stack */
185 static DataValue *StackP; /* next free spot on stack */
186 static DataValue *FrameP; /* frame pointer (start of local variables
187 for the current subroutine invocation) */
188 static Inst *PC; /* program counter during execution */
189 static char *ErrMsg; /* global for returning error messages
190 from executing functions */
191 static WindowInfo
192 *InitiatingWindow = NULL; /* window from which macro was run */
193 static WindowInfo *FocusWindow; /* window on which macro commands operate */
194 static int PreemptRequest; /* passes preemption requests from called
195 routines back up to the interpreter */
197 /* Array for mapping operations to functions for performing the operations
198 Must correspond to the enum called "operations" in interpret.h */
199 static int (*OpFns[N_OPS])() = {returnNoVal, returnVal, pushSymVal, dupStack,
200 add, subtract, multiply, divide, modulo, negate, increment, decrement,
201 gt, lt, ge, le, eq, ne, bitAnd, bitOr, and, or, not, power, concat,
202 assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse,
203 branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
204 deleteArrayElement, pushArraySymVal,
205 arrayRefAndAssignSetup};
208 ** Initialize macro language global variables. Must be called before
209 ** any macros are even parsed, because the parser uses action routine
210 ** symbols to comprehend hyphenated names.
212 void InitMacroGlobals(void)
214 XtActionsRec *actions;
215 int i, nActions;
216 static char argName[3] = "$x";
217 static DataValue dv = {NO_TAG, {0}};
219 /* Add action routines from NEdit menus and text widget */
220 actions = GetMenuActions(&nActions);
221 for (i=0; i<nActions; i++) {
222 dv.val.xtproc = actions[i].proc;
223 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv);
225 actions = TextGetActions(&nActions);
226 for (i=0; i<nActions; i++) {
227 dv.val.xtproc = actions[i].proc;
228 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv);
231 /* Add subroutine argument symbols ($1, $2, ..., $9) */
232 for (i=0; i<9; i++) {
233 argName[1] = '1' + i;
234 dv.val.n = i;
235 InstallSymbol(argName, ARG_SYM, dv);
238 /* Add special symbol $n_args */
239 dv.val.n = N_ARGS_ARG_SYM;
240 InstallSymbol("$n_args", ARG_SYM, dv);
244 ** To build a program for the interpreter, call BeginCreatingProgram, to
245 ** begin accumulating the program, followed by calls to AddOp, AddSym,
246 ** and InstallSymbol to add symbols and operations. When the new program
247 ** is finished, collect the results with FinishCreatingProgram. This returns
248 ** a self contained program that can be run with ExecuteMacro.
252 ** Start collecting instructions for a program. Clears the program
253 ** and the symbol table.
255 void BeginCreatingProgram(void)
257 LocalSymList = NULL;
258 ProgP = Prog;
259 LoopStackPtr = LoopStack;
263 ** Finish up the program under construction, and return it (code and
264 ** symbol table) as a package that ExecuteMacro can execute. This
265 ** program must be freed with FreeProgram.
267 Program *FinishCreatingProgram(void)
269 Program *newProg;
270 int progLen, fpOffset = 0;
271 Symbol *s;
273 newProg = (Program *)XtMalloc(sizeof(Program));
274 progLen = ((char *)ProgP) - ((char *)Prog);
275 newProg->code = (Inst *)XtMalloc(progLen);
276 memcpy(newProg->code, Prog, progLen);
277 newProg->localSymList = LocalSymList;
278 LocalSymList = NULL;
280 /* Local variables' values are stored on the stack. Here we assign
281 frame pointer offsets to them. */
282 for (s = newProg->localSymList; s != NULL; s = s->next)
283 s->value.val.n = fpOffset++;
285 DISASM(newProg->code, ProgP - Prog);
287 return newProg;
290 void FreeProgram(Program *prog)
292 freeSymbolTable(prog->localSymList);
293 XtFree((char *)prog->code);
294 XtFree((char *)prog);
298 ** Add an operator (instruction) to the end of the current program
300 int AddOp(int op, char **msg)
302 if (ProgP >= &Prog[PROGRAM_SIZE]) {
303 *msg = "macro too large";
304 return 0;
306 *ProgP++ = OpFns[op];
307 return 1;
311 ** Add a symbol operand to the current program
313 int AddSym(Symbol *sym, char **msg)
315 if (ProgP >= &Prog[PROGRAM_SIZE]) {
316 *msg = "macro too large";
317 return 0;
319 *ProgP++ = (Inst)sym;
320 return 1;
324 ** Add an immediate value operand to the current program
326 int AddImmediate(void *value, char **msg)
328 if (ProgP >= &Prog[PROGRAM_SIZE]) {
329 *msg = "macro too large";
330 return 0;
332 *ProgP++ = (Inst)value;
333 return 1;
337 ** Add a branch offset operand to the current program
339 int AddBranchOffset(Inst *to, char **msg)
341 if (ProgP >= &Prog[PROGRAM_SIZE]) {
342 *msg = "macro too large";
343 return 0;
345 *ProgP = (Inst)(to - ProgP);
346 ProgP++;
348 return 1;
352 ** Return the address at which the next instruction will be stored
354 Inst *GetPC(void)
356 return ProgP;
360 ** Swap the positions of two contiguous blocks of code. The first block
361 ** running between locations start and boundary, and the second between
362 ** boundary and end.
364 void SwapCode(Inst *start, Inst *boundary, Inst *end)
366 #define reverseCode(L, H) \
367 do { register Inst t, *l = L, *h = H - 1; \
368 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
369 /* double-reverse method: reverse elements of both parts then whole lot */
370 /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
371 reverseCode(start, boundary); /* 1 */
372 reverseCode(boundary, end); /* 2 */
373 reverseCode(start, end); /* 3 */
377 ** Maintain a stack to save addresses of branch operations for break and
378 ** continue statements, so they can be filled in once the information
379 ** on where to branch is known.
381 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or
382 ** AddContinueAddr to register the address at which to store the branch
383 ** address for a break or continue statement, and FillLoopAddrs to fill
384 ** in all the addresses and return to the level of the enclosing loop.
386 void StartLoopAddrList(void)
388 addLoopAddr(NULL);
391 int AddBreakAddr(Inst *addr)
393 if (LoopStackPtr == LoopStack) return 1;
394 addLoopAddr(addr);
395 *addr = NEEDS_BREAK;
396 return 0;
399 int AddContinueAddr(Inst *addr)
401 if (LoopStackPtr == LoopStack) return 1;
402 addLoopAddr(addr);
403 *addr = NEEDS_CONTINUE;
404 return 0;
407 static void addLoopAddr(Inst *addr)
409 if (LoopStackPtr > &LoopStack[LOOP_STACK_SIZE-1]) {
410 fprintf(stderr, "NEdit: loop stack overflow in macro parser");
411 return;
413 *LoopStackPtr++ = addr;
416 void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr)
418 while (True) {
419 LoopStackPtr--;
420 if (LoopStackPtr < LoopStack) {
421 fprintf(stderr, "NEdit: internal error (lsu) in macro parser\n");
422 return;
424 if (*LoopStackPtr == NULL)
425 break;
426 if (**LoopStackPtr == NEEDS_BREAK)
427 **(Inst ***)LoopStackPtr = (Inst *)(breakAddr - *LoopStackPtr);
428 else if (**LoopStackPtr == NEEDS_CONTINUE)
429 **(Inst ***)LoopStackPtr = (Inst *)(continueAddr - *LoopStackPtr);
430 else
431 fprintf(stderr, "NEdit: internal error (uat) in macro parser\n");
436 ** Execute a compiled macro, "prog", using the arguments in the array
437 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.
438 ** if MACRO_DONE is returned, the macro completed, and the returned value
439 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the
440 ** macro exceeded its alotted time-slice and scheduled...
442 int ExecuteMacro(WindowInfo *window, Program *prog, int nArgs, DataValue *args,
443 DataValue *result, RestartData **continuation, char **msg)
445 RestartData *context;
446 static DataValue noValue = {NO_TAG, {0}};
447 Symbol *s;
448 int i;
450 /* Create an execution context (a stack, a stack pointer, a frame pointer,
451 and a program counter) which will retain the program state across
452 preemption and resumption of execution */
453 context = (RestartData *)XtMalloc(sizeof(RestartData));
454 context->stack = (DataValue *)XtMalloc(sizeof(DataValue) * STACK_SIZE);
455 *continuation = context;
456 context->stackP = context->stack;
457 context->pc = prog->code;
458 context->runWindow = window;
459 context->focusWindow = window;
461 /* Push arguments and call information onto the stack */
462 for (i=0; i<nArgs; i++)
463 *(context->stackP++) = args[i];
464 context->stackP->val.subr = NULL;
465 context->stackP->tag = NO_TAG;
466 context->stackP++;
467 *(context->stackP++) = noValue;
468 context->stackP->tag = NO_TAG;
469 context->stackP->val.n = nArgs;
470 context->stackP++;
471 context->frameP = context->stackP;
473 /* Initialize and make room on the stack for local variables */
474 for (s = prog->localSymList; s != NULL; s = s->next) {
475 *(context->frameP + s->value.val.n) = noValue;
476 context->stackP++;
479 /* Begin execution, return on error or preemption */
480 return ContinueMacro(context, result, msg);
484 ** Continue the execution of a suspended macro whose state is described in
485 ** "continuation"
487 int ContinueMacro(RestartData *continuation, DataValue *result, char **msg)
489 register int status, instCount = 0;
490 register Inst *inst;
491 RestartData oldContext;
493 /* To allow macros to be invoked arbitrarily (such as those automatically
494 triggered within smart-indent) within executing macros, this call is
495 reentrant. */
496 saveContext(&oldContext);
499 ** Execution Loop: Call the succesive routine addresses in the program
500 ** until one returns something other than STAT_OK, then take action
502 restoreContext(continuation);
503 ErrMsg = NULL;
504 for (;;) {
506 /* Execute an instruction */
507 inst = PC++;
508 status = (*inst)();
510 /* If error return was not STAT_OK, return to caller */
511 if (status != STAT_OK) {
512 if (status == STAT_PREEMPT) {
513 saveContext(continuation);
514 restoreContext(&oldContext);
515 return MACRO_PREEMPT;
516 } else if (status == STAT_ERROR) {
517 *msg = ErrMsg;
518 FreeRestartData(continuation);
519 restoreContext(&oldContext);
520 return MACRO_ERROR;
521 } else if (status == STAT_DONE) {
522 *msg = "";
523 *result = *--StackP;
524 FreeRestartData(continuation);
525 restoreContext(&oldContext);
526 return MACRO_DONE;
530 /* Count instructions executed. If the instruction limit is hit,
531 preempt, store re-start information in continuation and give
532 X, other macros, and other shell scripts a chance to execute */
533 instCount++;
534 if (instCount >= INSTRUCTION_LIMIT) {
535 saveContext(continuation);
536 restoreContext(&oldContext);
537 return MACRO_TIME_LIMIT;
543 ** If a macro is already executing, and requests that another macro be run,
544 ** this can be called instead of ExecuteMacro to run it in the same context
545 ** as if it were a subroutine. This saves the caller from maintaining
546 ** separate contexts, and serializes processing of the two macros without
547 ** additional work.
549 void RunMacroAsSubrCall(Program *prog)
551 Symbol *s;
552 static DataValue noValue = {NO_TAG, {0}};
554 /* See subroutine "callSubroutine" for a description of the stack frame
555 for a subroutine call */
556 StackP->tag = NO_TAG;
557 StackP->val.inst = PC;
558 StackP++;
559 StackP->tag = NO_TAG;
560 StackP->val.dataval = FrameP;
561 StackP++;
562 StackP->tag = NO_TAG;
563 StackP->val.n = 0;
564 StackP++;
565 FrameP = StackP;
566 PC = prog->code;
567 for (s = prog->localSymList; s != NULL; s = s->next) {
568 *(FrameP + s->value.val.n) = noValue;
569 StackP++;
573 void FreeRestartData(RestartData *context)
575 XtFree((char *)context->stack);
576 XtFree((char *)context);
580 ** Cause a macro in progress to be preempted (called by commands which take
581 ** a long time, or want to return to the event loop. Call ResumeMacroExecution
582 ** to resume.
584 void PreemptMacro(void)
586 PreemptRequest = True;
590 ** Reset the return value for a subroutine which caused preemption (this is
591 ** how to return a value from a routine which preempts instead of returning
592 ** a value directly).
594 void ModifyReturnedValue(RestartData *context, DataValue dv)
596 if (*(context->pc-1) == fetchRetVal)
597 *(context->stackP-1) = dv;
601 ** Called within a routine invoked from a macro, returns the window in
602 ** which the macro is executing (where the banner is, not where it is focused)
604 WindowInfo *MacroRunWindow(void)
606 return InitiatingWindow;
610 ** Called within a routine invoked from a macro, returns the window to which
611 ** the currently executing macro is focused (the window which macro commands
612 ** modify, not the window from which the macro is being run)
614 WindowInfo *MacroFocusWindow(void)
616 return FocusWindow;
620 ** Set the window to which macro subroutines and actions which operate on an
621 ** implied window are directed.
623 void SetMacroFocusWindow(WindowInfo *window)
625 FocusWindow = window;
629 ** install an array iteration symbol
630 ** it is tagged as an integer but holds an array node pointer
632 Symbol *InstallIteratorSymbol()
634 char symbolName[10 + TYPE_INT_STR_SIZE(int)];
635 DataValue value;
636 static int interatorNameIndex = 0;
638 sprintf(symbolName, "aryiter #%d", interatorNameIndex);
639 ++interatorNameIndex;
640 value.tag = INT_TAG;
641 value.val.arrayPtr = NULL;
642 return(InstallSymbol(symbolName, LOCAL_SYM, value));
646 ** Lookup a constant string by its value. This allows reuse of string
647 ** constants and fixing a leak in the interpreter.
649 Symbol *LookupStringConstSymbol(const char *value)
651 Symbol *s;
653 for (s = GlobalSymList; s != NULL; s = s->next) {
654 if (s->type == CONST_SYM &&
655 s->value.tag == STRING_TAG &&
656 !strcmp(s->value.val.str, value)) {
657 return(s);
660 return(NULL);
664 ** install string str in the global symbol table with a string name
666 Symbol *InstallStringConstSymbol(const char *str)
668 static int stringConstIndex = 0;
669 char stringName[35];
670 DataValue value;
671 Symbol *sym = LookupStringConstSymbol(str);
672 if (sym) {
673 return sym;
676 sprintf(stringName, "string #%d", stringConstIndex++);
677 value.tag = STRING_TAG;
678 value.val.str = AllocStringCpy(str);
679 return(InstallSymbol(stringName, CONST_SYM, value));
683 ** find a symbol in the symbol table
685 Symbol *LookupSymbol(const char *name)
687 Symbol *s;
689 for (s = LocalSymList; s != NULL; s = s->next)
690 if (strcmp(s->name, name) == 0)
691 return s;
692 for (s = GlobalSymList; s != NULL; s = s->next)
693 if (strcmp(s->name, name) == 0)
694 return s;
695 return NULL;
699 ** install symbol name in symbol table
701 Symbol *InstallSymbol(const char *name, int type, DataValue value)
703 Symbol *s;
705 s = (Symbol *)malloc(sizeof(Symbol));
706 s->name = (char *)malloc(strlen(name)+1); /* +1 for '\0' */
707 strcpy(s->name, name);
708 s->type = type;
709 s->value = value;
710 if (type == LOCAL_SYM) {
711 s->next = LocalSymList;
712 LocalSymList = s;
713 } else {
714 s->next = GlobalSymList;
715 GlobalSymList = s;
717 return s;
721 ** Promote a symbol from local to global, removing it from the local symbol
722 ** list.
724 Symbol *PromoteToGlobal(Symbol *sym)
726 Symbol *s;
727 static DataValue noValue = {NO_TAG, {0}};
729 if (sym->type != LOCAL_SYM)
730 return sym;
732 /* Remove sym from the local symbol list */
733 if (sym == LocalSymList)
734 LocalSymList = sym->next;
735 else {
736 for (s = LocalSymList; s != NULL; s = s->next) {
737 if (s->next == sym) {
738 s->next = sym->next;
739 break;
744 s = LookupSymbol(sym->name);
745 if (s != NULL)
746 return s;
747 return InstallSymbol(sym->name, GLOBAL_SYM, noValue);
751 ** Allocate memory for a string, and keep track of it, such that it
752 ** can be recovered later using GarbageCollectStrings. (A linked list
753 ** of pointers is maintained by threading through the memory behind
754 ** the returned pointers). Length does not include the terminating null
755 ** character, so to allocate space for a string of strlen == n, you must
756 ** use AllocString(n+1).
759 /*#define TRACK_GARBAGE_LEAKS*/
760 #ifdef TRACK_GARBAGE_LEAKS
761 static int numAllocatedStrings = 0;
762 static int numAllocatedSparseArrayElements = 0;
763 #endif
765 /* Allocate a new string buffer of length chars */
766 char *AllocString(int length)
768 char *mem;
770 mem = XtMalloc(length + sizeof(char *) + 1);
771 *((char **)mem) = AllocatedStrings;
772 AllocatedStrings = mem;
773 #ifdef TRACK_GARBAGE_LEAKS
774 ++numAllocatedStrings;
775 #endif
776 return mem + sizeof(char *) + 1;
779 /* Allocate a new string buffer of length chars, and copy in the string s */
780 char *AllocStringNCpy(const char *s, int length)
782 char *p = AllocString(length + 1); /* add extra char for forced \0 */
783 if (!p)
784 return p;
785 if (!s)
786 s = "";
787 p[length] = '\0'; /* forced \0 */
788 return strncpy(p, s, length);
791 /* Allocate a new copy of string s */
792 char *AllocStringCpy(const char *s)
794 return AllocStringNCpy(s, s ? strlen(s) : 0);
797 static SparseArrayEntry *allocateSparseArrayEntry(void)
799 SparseArrayEntryWrapper *mem;
801 mem = (SparseArrayEntryWrapper *)XtMalloc(sizeof(SparseArrayEntryWrapper));
802 mem->next = (struct SparseArrayEntryWrapper *)AllocatedSparseArrayEntries;
803 AllocatedSparseArrayEntries = mem;
804 #ifdef TRACK_GARBAGE_LEAKS
805 ++numAllocatedSparseArrayElements;
806 #endif
807 return(&(mem->data));
810 static void MarkArrayContentsAsUsed(SparseArrayEntry *arrayPtr)
812 SparseArrayEntry *globalSEUse;
814 if (arrayPtr) {
815 ((SparseArrayEntryWrapper *)arrayPtr)->inUse = 1;
816 for (globalSEUse = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)arrayPtr);
817 globalSEUse != NULL;
818 globalSEUse = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)globalSEUse)) {
820 ((SparseArrayEntryWrapper *)globalSEUse)->inUse = 1;
821 /* test first because it may be read-only static string */
822 if (!(*(globalSEUse->key - 1))) {
823 *(globalSEUse->key - 1) = 1;
825 if (globalSEUse->value.tag == STRING_TAG) {
826 /* test first because it may be read-only static string */
827 if (!(*(globalSEUse->value.val.str - 1))) {
828 *(globalSEUse->value.val.str - 1) = 1;
831 else if (globalSEUse->value.tag == ARRAY_TAG) {
832 MarkArrayContentsAsUsed((SparseArrayEntry *)globalSEUse->value.val.arrayPtr);
839 ** Collect strings that are no longer referenced from the global symbol
840 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must
841 ** only be run after all macro activity has ceased.
844 void GarbageCollectStrings(void)
846 SparseArrayEntryWrapper *nextAP, *thisAP;
847 char *p, *next;
848 Symbol *s;
850 /* mark all strings as unreferenced */
851 for (p = AllocatedStrings; p != NULL; p = *((char **)p)) {
852 *(p + sizeof(char *)) = 0;
855 for (thisAP = AllocatedSparseArrayEntries;
856 thisAP != NULL; thisAP = (SparseArrayEntryWrapper *)thisAP->next) {
857 thisAP->inUse = 0;
860 /* Sweep the global symbol list, marking which strings are still
861 referenced */
862 for (s = GlobalSymList; s != NULL; s = s->next) {
863 if (s->value.tag == STRING_TAG) {
864 /* test first because it may be read-only static string */
865 if (!(*(s->value.val.str - 1))) {
866 *(s->value.val.str - 1) = 1;
869 else if (s->value.tag == ARRAY_TAG) {
870 MarkArrayContentsAsUsed((SparseArrayEntry *)s->value.val.arrayPtr);
874 /* Collect all of the strings which remain unreferenced */
875 next = AllocatedStrings;
876 AllocatedStrings = NULL;
877 while (next != NULL) {
878 p = next;
879 next = *((char **)p);
880 if (*(p + sizeof(char *)) != 0) {
881 *((char **)p) = AllocatedStrings;
882 AllocatedStrings = p;
884 else {
885 #ifdef TRACK_GARBAGE_LEAKS
886 --numAllocatedStrings;
887 #endif
888 XtFree(p);
892 nextAP = AllocatedSparseArrayEntries;
893 AllocatedSparseArrayEntries = NULL;
894 while (nextAP != NULL) {
895 thisAP = nextAP;
896 nextAP = (SparseArrayEntryWrapper *)nextAP->next;
897 if (thisAP->inUse != 0) {
898 thisAP->next = (struct SparseArrayEntryWrapper *)AllocatedSparseArrayEntries;
899 AllocatedSparseArrayEntries = thisAP;
901 else {
902 #ifdef TRACK_GARBAGE_LEAKS
903 --numAllocatedSparseArrayElements;
904 #endif
905 XtFree((void *)thisAP);
909 #ifdef TRACK_GARBAGE_LEAKS
910 printf("str count = %d\nary count = %d\n", numAllocatedStrings, numAllocatedSparseArrayElements);
911 #endif
915 ** Save and restore execution context to data structure "context"
917 static void saveContext(RestartData *context)
919 context->stack = Stack;
920 context->stackP = StackP;
921 context->frameP = FrameP;
922 context->pc = PC;
923 context->runWindow = InitiatingWindow;
924 context->focusWindow = FocusWindow;
927 static void restoreContext(RestartData *context)
929 Stack = context->stack;
930 StackP = context->stackP;
931 FrameP = context->frameP;
932 PC = context->pc;
933 InitiatingWindow = context->runWindow;
934 FocusWindow = context->focusWindow;
937 static void freeSymbolTable(Symbol *symTab)
939 Symbol *s;
941 while(symTab != NULL) {
942 s = symTab;
943 free(s->name);
944 symTab = s->next;
945 free((char *)s);
949 #define POP(dataVal) \
950 if (StackP == Stack) \
951 return execError(StackUnderflowMsg, ""); \
952 dataVal = *--StackP;
954 #define PUSH(dataVal) \
955 if (StackP >= &Stack[STACK_SIZE]) \
956 return execError(StackOverflowMsg, ""); \
957 *StackP++ = dataVal;
959 #define PEEK(dataVal, peekIndex) \
960 dataVal = *(StackP - peekIndex - 1);
962 #define POP_INT(number) \
963 if (StackP == Stack) \
964 return execError(StackUnderflowMsg, ""); \
965 --StackP; \
966 if (StackP->tag == STRING_TAG) { \
967 if (!StringToNum(StackP->val.str, &number)) \
968 return execError(StringToNumberMsg, ""); \
969 } else if (StackP->tag == INT_TAG) \
970 number = StackP->val.n; \
971 else \
972 return(execError("can't convert array to integer", NULL));
974 #define POP_STRING(string) \
975 if (StackP == Stack) \
976 return execError(StackUnderflowMsg, ""); \
977 --StackP; \
978 if (StackP->tag == INT_TAG) { \
979 string = AllocString(TYPE_INT_STR_SIZE(int)); \
980 sprintf(string, "%d", StackP->val.n); \
981 } else if (StackP->tag == STRING_TAG) \
982 string = StackP->val.str; \
983 else \
984 return(execError("can't convert array to string", NULL));
986 #define PEEK_STRING(string, peekIndex) \
987 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
988 string = AllocString(TYPE_INT_STR_SIZE(int)); \
989 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \
991 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
992 string = (StackP - peekIndex - 1)->val.str; \
994 else { \
995 return(execError("can't convert array to string", NULL)); \
998 #define PEEK_INT(number, peekIndex) \
999 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1000 if (!StringToNum((StackP - peekIndex - 1)->val.str, &number)) { \
1001 return execError(StringToNumberMsg, ""); \
1003 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1004 number = (StackP - peekIndex - 1)->val.n; \
1006 else { \
1007 return(execError("can't convert array to string", NULL)); \
1010 #define PUSH_INT(number) \
1011 if (StackP >= &Stack[STACK_SIZE]) \
1012 return execError(StackOverflowMsg, ""); \
1013 StackP->tag = INT_TAG; \
1014 StackP->val.n = number; \
1015 StackP++;
1017 #define PUSH_STRING(string) \
1018 if (StackP >= &Stack[STACK_SIZE]) \
1019 return execError(StackOverflowMsg, ""); \
1020 StackP->tag = STRING_TAG; \
1021 StackP->val.str = string; \
1022 StackP++;
1024 #define BINARY_NUMERIC_OPERATION(operator) \
1025 int n1, n2; \
1026 DISASM_RT(PC-1, 1); \
1027 STACKDUMP(2, 3); \
1028 POP_INT(n2) \
1029 POP_INT(n1) \
1030 PUSH_INT(n1 operator n2) \
1031 return STAT_OK;
1033 #define UNARY_NUMERIC_OPERATION(operator) \
1034 int n; \
1035 DISASM_RT(PC-1, 1); \
1036 STACKDUMP(1, 3); \
1037 POP_INT(n) \
1038 PUSH_INT(operator n) \
1039 return STAT_OK;
1042 ** copy a symbol's value onto the stack
1043 ** Before: Prog-> [Sym], next, ...
1044 ** Stack-> next, ...
1045 ** After: Prog-> Sym, [next], ...
1046 ** Stack-> [SymValue], next, ...
1048 static int pushSymVal(void)
1050 Symbol *s;
1051 int nArgs, argNum;
1053 DISASM_RT(PC-1, 2);
1054 STACKDUMP(0, 3);
1056 s = (Symbol *)*PC++;
1057 if (s->type == LOCAL_SYM) {
1058 *StackP = *(FrameP + s->value.val.n);
1059 } else if (s->type == GLOBAL_SYM || s->type == CONST_SYM) {
1060 *StackP = s->value;
1061 } else if (s->type == ARG_SYM) {
1062 nArgs = (FrameP-1)->val.n;
1063 argNum = s->value.val.n;
1064 if (argNum >= nArgs)
1065 return execError("referenced undefined argument: %s", s->name);
1066 if (argNum == N_ARGS_ARG_SYM) {
1067 StackP->tag = INT_TAG;
1068 StackP->val.n = nArgs;
1069 } else
1070 *StackP = *(FrameP + argNum - nArgs - 3);
1071 } else if (s->type == PROC_VALUE_SYM) {
1072 DataValue result;
1073 char *errMsg;
1074 if (!(s->value.val.subr)(FocusWindow, NULL, 0,
1075 &result, &errMsg))
1076 return execError(errMsg, s->name);
1077 *StackP = result;
1078 } else
1079 return execError("reading non-variable: %s", s->name);
1080 if (StackP->tag == NO_TAG)
1081 return execError("variable not set: %s", s->name);
1082 StackP++;
1083 if (StackP >= &Stack[STACK_SIZE])
1084 return execError(StackOverflowMsg, "");
1085 return STAT_OK;
1089 ** Push an array (by reference) onto the stack
1090 ** Before: Prog-> [ArraySym], makeEmpty, next, ...
1091 ** Stack-> next, ...
1092 ** After: Prog-> ArraySym, makeEmpty, [next], ...
1093 ** Stack-> [elemValue], next, ...
1094 ** makeEmpty is either true (1) or false (0): if true, and the element is not
1095 ** present in the array, create it.
1097 static int pushArraySymVal(void)
1099 Symbol *sym;
1100 DataValue *dataPtr;
1101 int initEmpty;
1103 DISASM_RT(PC-1, 3);
1104 STACKDUMP(0, 3);
1106 sym = (Symbol *)*PC;
1107 PC++;
1108 initEmpty = (int)*PC;
1109 PC++;
1111 if (sym->type == LOCAL_SYM) {
1112 dataPtr = FrameP + sym->value.val.n;
1113 } else if (sym->type == GLOBAL_SYM) {
1114 dataPtr = &sym->value;
1115 } else {
1116 return execError("assigning to non-lvalue array or non-array: %s", sym->name);
1119 if (initEmpty && dataPtr->tag == NO_TAG) {
1120 dataPtr->tag = ARRAY_TAG;
1121 dataPtr->val.arrayPtr = ArrayNew();
1124 if (dataPtr->tag == NO_TAG) {
1125 return execError("variable not set: %s", sym->name);
1128 *StackP = *dataPtr;
1129 StackP++;
1131 if (StackP >= &Stack[STACK_SIZE]) {
1132 return execError(StackOverflowMsg, "");
1134 return(STAT_OK);
1138 ** assign top value to next symbol
1140 ** Before: Prog-> [symbol], next, ...
1141 ** Stack-> [value], next, ...
1142 ** After: Prog-> symbol, [next], ...
1143 ** Stack-> next, ...
1145 static int assign(void)
1147 Symbol *sym;
1148 DataValue *dataPtr;
1150 DISASM_RT(PC-1, 2);
1151 STACKDUMP(1, 3);
1153 sym = (Symbol *)(*PC++);
1154 if (sym->type != GLOBAL_SYM && sym->type != LOCAL_SYM) {
1155 if (sym->type == ARG_SYM)
1156 return execError("assignment to function argument: %s", sym->name);
1157 else if (sym->type == PROC_VALUE_SYM)
1158 return execError("assignment to read-only variable: %s", sym->name);
1159 else
1160 return execError("assignment to non-variable: %s", sym->name);
1162 if (StackP == Stack)
1163 return execError(StackUnderflowMsg, "");
1164 --StackP;
1165 if (sym->type == LOCAL_SYM)
1166 dataPtr = (FrameP + sym->value.val.n);
1167 else
1168 dataPtr = &sym->value;
1169 if (StackP->tag == ARRAY_TAG) {
1170 ArrayCopy(dataPtr, StackP);
1172 else {
1173 *dataPtr = *StackP;
1175 return STAT_OK;
1179 ** copy the top value of the stack
1180 ** Before: Stack-> value, next, ...
1181 ** After: Stack-> value, value, next, ...
1183 static int dupStack(void)
1185 DISASM_RT(PC-1, 1);
1186 STACKDUMP(1, 3);
1188 if (StackP >= &Stack[STACK_SIZE])
1189 return execError(StackOverflowMsg, "");
1190 *StackP = *(StackP - 1);
1191 StackP++;
1192 return STAT_OK;
1196 ** if left and right arguments are arrays, then the result is a new array
1197 ** in which all the keys from both the right and left are copied
1198 ** the values from the right array are used in the result array when the
1199 ** keys are the same
1200 ** Before: Stack-> value2, value1, next, ...
1201 ** After: Stack-> resValue, next, ...
1203 static int add(void)
1205 DataValue leftVal, rightVal, resultArray;
1206 int n1, n2;
1208 DISASM_RT(PC-1, 1);
1209 STACKDUMP(2, 3);
1211 PEEK(rightVal, 0)
1212 if (rightVal.tag == ARRAY_TAG) {
1213 PEEK(leftVal, 1)
1214 if (leftVal.tag == ARRAY_TAG) {
1215 SparseArrayEntry *leftIter, *rightIter;
1216 resultArray.tag = ARRAY_TAG;
1217 resultArray.val.arrayPtr = ArrayNew();
1219 POP(rightVal)
1220 POP(leftVal)
1221 leftIter = arrayIterateFirst(&leftVal);
1222 rightIter = arrayIterateFirst(&rightVal);
1223 while (leftIter || rightIter) {
1224 int insertResult = 1;
1226 if (leftIter && rightIter) {
1227 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1228 if (compareResult < 0) {
1229 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1230 leftIter = arrayIterateNext(leftIter);
1232 else if (compareResult > 0) {
1233 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1234 rightIter = arrayIterateNext(rightIter);
1236 else {
1237 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1238 leftIter = arrayIterateNext(leftIter);
1239 rightIter = arrayIterateNext(rightIter);
1242 else if (leftIter) {
1243 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1244 leftIter = arrayIterateNext(leftIter);
1246 else {
1247 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1248 rightIter = arrayIterateNext(rightIter);
1250 if (!insertResult) {
1251 return(execError("array insertion failure", NULL));
1254 PUSH(resultArray)
1256 else {
1257 return(execError("can't mix math with arrays and non-arrays", NULL));
1260 else {
1261 POP_INT(n2)
1262 POP_INT(n1)
1263 PUSH_INT(n1 + n2)
1265 return(STAT_OK);
1269 ** if left and right arguments are arrays, then the result is a new array
1270 ** in which only the keys which exist in the left array but not in the right
1271 ** are copied
1272 ** Before: Stack-> value2, value1, next, ...
1273 ** After: Stack-> resValue, next, ...
1275 static int subtract(void)
1277 DataValue leftVal, rightVal, resultArray;
1278 int n1, n2;
1280 DISASM_RT(PC-1, 1);
1281 STACKDUMP(2, 3);
1283 PEEK(rightVal, 0)
1284 if (rightVal.tag == ARRAY_TAG) {
1285 PEEK(leftVal, 1)
1286 if (leftVal.tag == ARRAY_TAG) {
1287 SparseArrayEntry *leftIter, *rightIter;
1288 resultArray.tag = ARRAY_TAG;
1289 resultArray.val.arrayPtr = ArrayNew();
1291 POP(rightVal)
1292 POP(leftVal)
1293 leftIter = arrayIterateFirst(&leftVal);
1294 rightIter = arrayIterateFirst(&rightVal);
1295 while (leftIter) {
1296 int insertResult = 1;
1298 if (leftIter && rightIter) {
1299 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1300 if (compareResult < 0) {
1301 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1302 leftIter = arrayIterateNext(leftIter);
1304 else if (compareResult > 0) {
1305 rightIter = arrayIterateNext(rightIter);
1307 else {
1308 leftIter = arrayIterateNext(leftIter);
1309 rightIter = arrayIterateNext(rightIter);
1312 else if (leftIter) {
1313 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1314 leftIter = arrayIterateNext(leftIter);
1316 if (!insertResult) {
1317 return(execError("array insertion failure", NULL));
1320 PUSH(resultArray)
1322 else {
1323 return(execError("can't mix math with arrays and non-arrays", NULL));
1326 else {
1327 POP_INT(n2)
1328 POP_INT(n1)
1329 PUSH_INT(n1 - n2)
1331 return(STAT_OK);
1335 ** Other binary operators
1336 ** Before: Stack-> value2, value1, next, ...
1337 ** After: Stack-> resValue, next, ...
1339 ** Other unary operators
1340 ** Before: Stack-> value, next, ...
1341 ** After: Stack-> resValue, next, ...
1343 static int multiply(void)
1345 BINARY_NUMERIC_OPERATION(*)
1348 static int divide(void)
1350 int n1, n2;
1352 DISASM_RT(PC-1, 1);
1353 STACKDUMP(2, 3);
1355 POP_INT(n2)
1356 POP_INT(n1)
1357 if (n2 == 0) {
1358 return execError("division by zero", "");
1360 PUSH_INT(n1 / n2)
1361 return STAT_OK;
1364 static int modulo(void)
1366 int n1, n2;
1368 DISASM_RT(PC-1, 1);
1369 STACKDUMP(2, 3);
1371 POP_INT(n2)
1372 POP_INT(n1)
1373 if (n2 == 0) {
1374 return execError("modulo by zero", "");
1376 PUSH_INT(n1 % n2)
1377 return STAT_OK;
1380 static int negate(void)
1382 UNARY_NUMERIC_OPERATION(-)
1385 static int increment(void)
1387 UNARY_NUMERIC_OPERATION(++)
1390 static int decrement(void)
1392 UNARY_NUMERIC_OPERATION(--)
1395 static int gt(void)
1397 BINARY_NUMERIC_OPERATION(>)
1400 static int lt(void)
1402 BINARY_NUMERIC_OPERATION(<)
1405 static int ge(void)
1407 BINARY_NUMERIC_OPERATION(>=)
1410 static int le(void)
1412 BINARY_NUMERIC_OPERATION(<=)
1416 ** verify that compares are between integers and/or strings only
1417 ** Before: Stack-> value1, value2, next, ...
1418 ** After: Stack-> resValue, next, ...
1419 ** where resValue is 1 for true, 0 for false
1421 static int eq(void)
1423 DataValue v1, v2;
1425 DISASM_RT(PC-1, 1);
1426 STACKDUMP(2, 3);
1428 POP(v1)
1429 POP(v2)
1430 if (v1.tag == INT_TAG && v2.tag == INT_TAG) {
1431 v1.val.n = v1.val.n == v2.val.n;
1433 else if (v1.tag == STRING_TAG && v2.tag == STRING_TAG) {
1434 v1.val.n = !strcmp(v1.val.str, v2.val.str);
1436 else if (v1.tag == STRING_TAG && v2.tag == INT_TAG) {
1437 int number;
1438 if (!StringToNum(v1.val.str, &number)) {
1439 v1.val.n = 0;
1441 else {
1442 v1.val.n = number == v2.val.n;
1445 else if (v2.tag == STRING_TAG && v1.tag == INT_TAG) {
1446 int number;
1447 if (!StringToNum(v2.val.str, &number)) {
1448 v1.val.n = 0;
1450 else {
1451 v1.val.n = number == v1.val.n;
1454 else {
1455 return(execError("incompatible types to compare", NULL));
1457 v1.tag = INT_TAG;
1458 PUSH(v1)
1459 return(STAT_OK);
1462 /* negated eq() call */
1463 static int ne(void)
1465 eq();
1466 return not();
1470 ** if left and right arguments are arrays, then the result is a new array
1471 ** in which only the keys which exist in both the right or left are copied
1472 ** the values from the right array are used in the result array
1473 ** Before: Stack-> value2, value1, next, ...
1474 ** After: Stack-> resValue, next, ...
1476 static int bitAnd(void)
1478 DataValue leftVal, rightVal, resultArray;
1479 int n1, n2;
1481 DISASM_RT(PC-1, 1);
1482 STACKDUMP(2, 3);
1484 PEEK(rightVal, 0)
1485 if (rightVal.tag == ARRAY_TAG) {
1486 PEEK(leftVal, 1)
1487 if (leftVal.tag == ARRAY_TAG) {
1488 SparseArrayEntry *leftIter, *rightIter;
1489 resultArray.tag = ARRAY_TAG;
1490 resultArray.val.arrayPtr = ArrayNew();
1492 POP(rightVal)
1493 POP(leftVal)
1494 leftIter = arrayIterateFirst(&leftVal);
1495 rightIter = arrayIterateFirst(&rightVal);
1496 while (leftIter && rightIter) {
1497 int insertResult = 1;
1498 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1500 if (compareResult < 0) {
1501 leftIter = arrayIterateNext(leftIter);
1503 else if (compareResult > 0) {
1504 rightIter = arrayIterateNext(rightIter);
1506 else {
1507 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1508 leftIter = arrayIterateNext(leftIter);
1509 rightIter = arrayIterateNext(rightIter);
1511 if (!insertResult) {
1512 return(execError("array insertion failure", NULL));
1515 PUSH(resultArray)
1517 else {
1518 return(execError("can't mix math with arrays and non-arrays", NULL));
1521 else {
1522 POP_INT(n2)
1523 POP_INT(n1)
1524 PUSH_INT(n1 & n2)
1526 return(STAT_OK);
1530 ** if left and right arguments are arrays, then the result is a new array
1531 ** in which only the keys which exist in either the right or left but not both
1532 ** are copied
1533 ** Before: Stack-> value2, value1, next, ...
1534 ** After: Stack-> resValue, next, ...
1536 static int bitOr(void)
1538 DataValue leftVal, rightVal, resultArray;
1539 int n1, n2;
1541 DISASM_RT(PC-1, 1);
1542 STACKDUMP(2, 3);
1544 PEEK(rightVal, 0)
1545 if (rightVal.tag == ARRAY_TAG) {
1546 PEEK(leftVal, 1)
1547 if (leftVal.tag == ARRAY_TAG) {
1548 SparseArrayEntry *leftIter, *rightIter;
1549 resultArray.tag = ARRAY_TAG;
1550 resultArray.val.arrayPtr = ArrayNew();
1552 POP(rightVal)
1553 POP(leftVal)
1554 leftIter = arrayIterateFirst(&leftVal);
1555 rightIter = arrayIterateFirst(&rightVal);
1556 while (leftIter || rightIter) {
1557 int insertResult = 1;
1559 if (leftIter && rightIter) {
1560 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1561 if (compareResult < 0) {
1562 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1563 leftIter = arrayIterateNext(leftIter);
1565 else if (compareResult > 0) {
1566 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1567 rightIter = arrayIterateNext(rightIter);
1569 else {
1570 leftIter = arrayIterateNext(leftIter);
1571 rightIter = arrayIterateNext(rightIter);
1574 else if (leftIter) {
1575 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1576 leftIter = arrayIterateNext(leftIter);
1578 else {
1579 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1580 rightIter = arrayIterateNext(rightIter);
1582 if (!insertResult) {
1583 return(execError("array insertion failure", NULL));
1586 PUSH(resultArray)
1588 else {
1589 return(execError("can't mix math with arrays and non-arrays", NULL));
1592 else {
1593 POP_INT(n2)
1594 POP_INT(n1)
1595 PUSH_INT(n1 | n2)
1597 return(STAT_OK);
1600 static int and(void)
1602 BINARY_NUMERIC_OPERATION(&&)
1605 static int or(void)
1607 BINARY_NUMERIC_OPERATION(||)
1610 static int not(void)
1612 UNARY_NUMERIC_OPERATION(!)
1616 ** raise one number to the power of another
1617 ** Before: Stack-> raisedBy, number, next, ...
1618 ** After: Stack-> result, next, ...
1620 static int power(void)
1622 int n1, n2, n3;
1624 DISASM_RT(PC-1, 1);
1625 STACKDUMP(2, 3);
1627 POP_INT(n2)
1628 POP_INT(n1)
1629 /* We need to round to deal with pow() giving results slightly above
1630 or below the real result since it deals with floating point numbers.
1631 Note: We're not really wanting rounded results, we merely
1632 want to deal with this simple issue. So, 2^-2 = .5, but we
1633 don't want to round this to 1. This is mainly intended to deal with
1634 4^2 = 15.999996 and 16.000001.
1636 if (n2 < 0 && n1 != 1 && n1 != -1) {
1637 if (n1 != 0) {
1638 /* since we're integer only, nearly all negative exponents result in 0 */
1639 n3 = 0;
1641 else {
1642 /* allow error to occur */
1643 n3 = (int)pow((double)n1, (double)n2);
1646 else {
1647 if ((n1 < 0) && (n2 & 1)) {
1648 /* round to nearest integer for negative values*/
1649 n3 = (int)(pow((double)n1, (double)n2) - (double)0.5);
1651 else {
1652 /* round to nearest integer for positive values*/
1653 n3 = (int)(pow((double)n1, (double)n2) + (double)0.5);
1656 PUSH_INT(n3)
1657 return errCheck("exponentiation");
1661 ** concatenate two top items on the stack
1662 ** Before: Stack-> str2, str1, next, ...
1663 ** After: Stack-> result, next, ...
1665 static int concat(void)
1667 char *s1, *s2, *out;
1668 int len1, len2;
1670 DISASM_RT(PC-1, 1);
1671 STACKDUMP(2, 3);
1673 POP_STRING(s2)
1674 POP_STRING(s1)
1675 len1 = strlen(s1);
1676 len2 = strlen(s2);
1677 out = AllocString(len1 + len2 + 1);
1678 strncpy(out, s1, len1);
1679 strcpy(&out[len1], s2);
1680 PUSH_STRING(out)
1681 return STAT_OK;
1685 ** Call a subroutine or function (user defined or built-in). Args are the
1686 ** subroutine's symbol, and the number of arguments which have been pushed
1687 ** on the stack.
1689 ** For a macro subroutine, the return address, frame pointer, number of
1690 ** arguments and space for local variables are added to the stack, and the
1691 ** PC is set to point to the new function. For a built-in routine, the
1692 ** arguments are popped off the stack, and the routine is just called.
1694 ** Before: Prog-> [subrSym], nArgs, next, ...
1695 ** Stack-> argN-arg1, next, ...
1696 ** After: Prog-> next, ... -- (built-in called subr)
1697 ** Stack-> retVal?, next, ...
1698 ** or: Prog-> (in called)next, ... -- (macro code called subr)
1699 ** Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argN-arg1, next, ...
1701 static int callSubroutine(void)
1703 Symbol *sym, *s;
1704 int i, nArgs;
1705 static DataValue noValue = {NO_TAG, {0}};
1706 Program *prog;
1707 char *errMsg;
1709 sym = (Symbol *)*PC++;
1710 nArgs = (int)*PC++;
1712 DISASM_RT(PC-3, 3);
1713 STACKDUMP(nArgs, 3);
1715 if (nArgs > MAX_ARGS)
1716 return execError("too many arguments to subroutine %s (max 9)",
1717 sym->name);
1720 ** If the subroutine is built-in, call the built-in routine
1722 if (sym->type == C_FUNCTION_SYM) {
1723 DataValue result;
1725 /* "pop" stack back to the first argument in the call stack */
1726 StackP -= nArgs;
1728 /* Call the function and check for preemption */
1729 PreemptRequest = False;
1730 if (!sym->value.val.subr(FocusWindow, StackP,
1731 nArgs, &result, &errMsg))
1732 return execError(errMsg, sym->name);
1733 if (*PC == fetchRetVal) {
1734 if (result.tag == NO_TAG)
1735 return execError("%s does not return a value", sym->name);
1736 PUSH(result);
1737 PC++;
1739 return PreemptRequest ? STAT_PREEMPT : STAT_OK;
1743 ** Call a macro subroutine:
1745 ** Push all of the required information to resume, and make space on the
1746 ** stack for local variables (and initialize them), on top of the argument
1747 ** values which are already there.
1749 if (sym->type == MACRO_FUNCTION_SYM) {
1750 StackP->tag = NO_TAG;
1751 StackP->val.inst = PC;
1752 StackP++;
1753 StackP->tag = NO_TAG;
1754 StackP->val.dataval = FrameP;
1755 StackP++;
1756 StackP->tag = NO_TAG;
1757 StackP->val.n = nArgs;
1758 StackP++;
1759 FrameP = StackP;
1760 prog = (Program *)sym->value.val.str;
1761 PC = prog->code;
1762 for (s = prog->localSymList; s != NULL; s = s->next) {
1763 *(FrameP + s->value.val.n) = noValue;
1764 StackP++;
1766 return STAT_OK;
1770 ** Call an action routine
1772 if (sym->type == ACTION_ROUTINE_SYM) {
1773 String argList[MAX_ARGS];
1774 Cardinal numArgs = nArgs;
1775 XKeyEvent key_event;
1776 Display *disp;
1777 Window win;
1779 /* Create a fake event with a timestamp suitable for actions which need
1780 timestamps, a marker to indicate that the call was from a macro
1781 (to stop shell commands from putting up their own separate banner) */
1782 disp=XtDisplay(InitiatingWindow->shell);
1783 win=XtWindow(InitiatingWindow->shell);
1785 key_event.type = KeyPress;
1786 key_event.send_event = MACRO_EVENT_MARKER;
1787 key_event.time=XtLastTimestampProcessed(XtDisplay(InitiatingWindow->shell));
1789 /* The following entries are just filled in to avoid problems
1790 in strange cases, like calling "self_insert()" directly from the
1791 macro menu. In fact the display was sufficient to cure this crash. */
1792 key_event.display=disp;
1793 key_event.window=key_event.root=key_event.subwindow=win;
1795 /* pop arguments off the stack and put them in the argument list */
1796 for (i=nArgs-1; i>=0; i--) {
1797 POP_STRING(argList[i])
1800 /* Call the action routine and check for preemption */
1801 PreemptRequest = False;
1802 sym->value.val.xtproc(FocusWindow->lastFocus,
1803 (XEvent *)&key_event, argList, &numArgs);
1804 if (*PC == fetchRetVal)
1805 return execError("%s does not return a value", sym->name);
1806 return PreemptRequest ? STAT_PREEMPT : STAT_OK;
1809 /* Calling a non subroutine symbol */
1810 return execError("%s is not a function or subroutine", sym->name);
1814 ** This should never be executed, returnVal checks for the presence of this
1815 ** instruction at the PC to decide whether to push the function's return
1816 ** value, then skips over it without executing.
1818 static int fetchRetVal(void)
1820 return execError("internal error: frv", NULL);
1823 /* see comments for returnValOrNone() */
1824 static int returnNoVal(void)
1826 return returnValOrNone(False);
1828 static int returnVal(void)
1830 return returnValOrNone(True);
1834 ** Return from a subroutine call
1835 ** Before: Prog-> [next], ...
1836 ** Stack-> retVal?, ...(FP), nArgs, oldFP, retPC, argN-arg1, next, ...
1837 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
1838 ** Stack-> retVal?, next, ...
1840 static int returnValOrNone(int valOnStack)
1842 DataValue retVal;
1843 static DataValue noValue = {NO_TAG, {0}};
1844 int nArgs;
1846 DISASM_RT(PC-1, 1);
1847 STACKDUMP(StackP - FrameP + FrameP[-1].val.n + 3, 3);
1849 /* return value is on the stack */
1850 if (valOnStack) {
1851 POP(retVal);
1854 /* pop past local variables */
1855 StackP = FrameP;
1857 /* get stored return information */
1858 nArgs = (--StackP)->val.n;
1859 FrameP = (--StackP)->val.dataval;
1860 PC = (--StackP)->val.inst;
1862 /* pop past function arguments */
1863 StackP -= nArgs;
1865 /* push returned value, if requsted */
1866 if (PC == NULL) {
1867 if (valOnStack) {
1868 PUSH(retVal);
1869 } else {
1870 PUSH(noValue);
1872 } else if (*PC == fetchRetVal) {
1873 if (valOnStack) {
1874 PUSH(retVal);
1875 PC++;
1876 } else {
1877 return execError(
1878 "using return value of %s which does not return a value",
1879 ((Symbol *)*(PC - 2))->name);
1883 /* NULL return PC indicates end of program */
1884 return PC == NULL ? STAT_DONE : STAT_OK;
1888 ** Unconditional branch offset by immediate operand
1890 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
1891 ** After: Prog-> branchDest, next, ..., (branchdest)[next]
1893 static int branch(void)
1895 DISASM_RT(PC-1, 2);
1896 STACKDUMP(0, 3);
1898 PC += (int)*PC;
1899 return STAT_OK;
1903 ** Conditional branches if stack value is True/False (non-zero/0) to address
1904 ** of immediate operand (pops stack)
1906 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
1907 ** After: either: Prog-> branchDest, [next], ...
1908 ** After: or: Prog-> branchDest, next, ..., (branchdest)[next]
1910 static int branchTrue(void)
1912 int value;
1913 Inst *addr;
1915 DISASM_RT(PC-1, 2);
1916 STACKDUMP(1, 3);
1918 POP_INT(value)
1919 addr = PC + (int)*PC;
1920 PC++;
1922 if (value)
1923 PC = addr;
1924 return STAT_OK;
1926 static int branchFalse(void)
1928 int value;
1929 Inst *addr;
1931 DISASM_RT(PC-1, 2);
1932 STACKDUMP(1, 3);
1934 POP_INT(value)
1935 addr = PC + (int)*PC;
1936 PC++;
1938 if (!value)
1939 PC = addr;
1940 return STAT_OK;
1944 ** Ignore the address following the instruction and continue. Why? So
1945 ** some code that uses conditional branching doesn't have to figure out
1946 ** whether to store a branch address.
1948 ** Before: Prog-> [branchDest], next, ...
1949 ** After: Prog-> branchDest, [next], ...
1951 static int branchNever(void)
1953 DISASM_RT(PC-1, 2);
1954 STACKDUMP(0, 3);
1956 PC++;
1957 return STAT_OK;
1961 ** recursively copy(duplicate) the sparse array nodes of an array
1962 ** this does not duplicate the key/node data since they are never
1963 ** modified, only replaced
1965 int ArrayCopy(DataValue *dstArray, DataValue *srcArray)
1967 SparseArrayEntry *srcIter;
1969 dstArray->tag = ARRAY_TAG;
1970 dstArray->val.arrayPtr = ArrayNew();
1972 srcIter = arrayIterateFirst(srcArray);
1973 while (srcIter) {
1974 if (srcIter->value.tag == ARRAY_TAG) {
1975 int errNum;
1976 DataValue tmpArray;
1978 errNum = ArrayCopy(&tmpArray, &srcIter->value);
1979 if (errNum != STAT_OK) {
1980 return(errNum);
1982 if (!ArrayInsert(dstArray, srcIter->key, &tmpArray)) {
1983 return(execError("array copy failed", NULL));
1986 else {
1987 if (!ArrayInsert(dstArray, srcIter->key, &srcIter->value)) {
1988 return(execError("array copy failed", NULL));
1991 srcIter = arrayIterateNext(srcIter);
1993 return(STAT_OK);
1997 ** creates an allocated string of a single key for all the sub-scripts
1998 ** using ARRAY_DIM_SEP as a separator
1999 ** this function uses the PEEK macros in order to remove most limits on
2000 ** the number of arguments to an array
2001 ** I really need to optimize the size approximation rather than assuming
2002 ** a worst case size for every integer argument
2004 static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams)
2006 DataValue tmpVal;
2007 int maxIntDigits = (sizeof(tmpVal.val.n) * 3) + 1;
2008 int sepLen = strlen(ARRAY_DIM_SEP);
2009 int keyLength = 0;
2010 int i;
2012 keyLength = sepLen * (nArgs - 1);
2013 for (i = nArgs - 1; i >= 0; --i) {
2014 PEEK(tmpVal, i)
2015 if (tmpVal.tag == INT_TAG) {
2016 keyLength += maxIntDigits;
2018 else if (tmpVal.tag == STRING_TAG) {
2019 keyLength += strlen(tmpVal.val.str);
2021 else {
2022 return(execError("can only index array with string or int.", NULL));
2025 *keyString = AllocString(keyLength + 1);
2026 (*keyString)[0] = 0;
2027 for (i = nArgs - 1; i >= 0; --i) {
2028 if (i != nArgs - 1) {
2029 strcat(*keyString, ARRAY_DIM_SEP);
2031 PEEK(tmpVal, i)
2032 if (tmpVal.tag == INT_TAG) {
2033 sprintf(&((*keyString)[strlen(*keyString)]), "%d", tmpVal.val.n);
2035 else if (tmpVal.tag == STRING_TAG) {
2036 strcat(*keyString, tmpVal.val.str);
2038 else {
2039 return(execError("can only index array with string or int.", NULL));
2042 if (!leaveParams) {
2043 for (i = nArgs - 1; i >= 0; --i) {
2044 POP(tmpVal)
2047 return(STAT_OK);
2051 ** allocate an empty array node, this is used as the root node and never
2052 ** contains any data, only refernces to other nodes
2054 static rbTreeNode *arrayEmptyAllocator(void)
2056 SparseArrayEntry *newNode = allocateSparseArrayEntry();
2057 if (newNode) {
2058 newNode->key = NULL;
2059 newNode->value.tag = NO_TAG;
2061 return((rbTreeNode *)newNode);
2065 ** create and copy array node and copy contents, we merely copy pointers
2066 ** since they are never modified, only replaced
2068 static rbTreeNode *arrayAllocateNode(rbTreeNode *src)
2070 SparseArrayEntry *newNode = allocateSparseArrayEntry();
2071 if (newNode) {
2072 newNode->key = ((SparseArrayEntry *)src)->key;
2073 newNode->value = ((SparseArrayEntry *)src)->value;
2075 return((rbTreeNode *)newNode);
2079 ** copy array node data, we merely copy pointers since they are never
2080 ** modified, only replaced
2082 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src)
2084 ((SparseArrayEntry *)dst)->key = ((SparseArrayEntry *)src)->key;
2085 ((SparseArrayEntry *)dst)->value = ((SparseArrayEntry *)src)->value;
2086 return(1);
2090 ** compare two array nodes returning an integer value similar to strcmp()
2092 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right)
2094 return(strcmp(((SparseArrayEntry *)left)->key, ((SparseArrayEntry *)right)->key));
2098 ** dispose an array node, garbage collection handles this, so we mark it
2099 ** to allow iterators in macro language to determine they have been unlinked
2101 static void arrayDisposeNode(rbTreeNode *src)
2103 /* Let garbage collection handle this but mark it so iterators can tell */
2104 src->left = NULL;
2105 src->right = NULL;
2106 src->parent = NULL;
2107 src->color = -1;
2110 struct SparseArrayEntry *ArrayNew(void)
2112 return((struct SparseArrayEntry *)rbTreeNew(arrayEmptyAllocator));
2116 ** insert a DataValue into an array, allocate the array if needed
2117 ** keyStr must be a string that was allocated with AllocString()
2119 int ArrayInsert(DataValue *theArray, char *keyStr, DataValue *theValue)
2121 SparseArrayEntry tmpEntry;
2122 rbTreeNode *insertedNode;
2124 tmpEntry.key = keyStr;
2125 tmpEntry.value = *theValue;
2127 if (theArray->val.arrayPtr == NULL) {
2128 theArray->val.arrayPtr = ArrayNew();
2130 if (theArray->val.arrayPtr != NULL) {
2131 insertedNode = rbTreeInsert((rbTreeNode *)(theArray->val.arrayPtr),
2132 (rbTreeNode *)&tmpEntry,
2133 arrayEntryCompare, arrayAllocateNode, arrayEntryCopyToNode);
2134 if (insertedNode) {
2135 return(1);
2137 else {
2138 return(0);
2141 return(0);
2145 ** remove a node from an array whose key matches keyStr
2147 void ArrayDelete(DataValue *theArray, char *keyStr)
2149 SparseArrayEntry searchEntry;
2151 if (theArray->val.arrayPtr) {
2152 searchEntry.key = keyStr;
2153 rbTreeDelete((rbTreeNode *)theArray->val.arrayPtr, (rbTreeNode *)&searchEntry,
2154 arrayEntryCompare, arrayDisposeNode);
2159 ** remove all nodes from an array
2161 void ArrayDeleteAll(DataValue *theArray)
2164 if (theArray->val.arrayPtr) {
2165 rbTreeNode *iter = rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
2166 while (iter) {
2167 rbTreeNode *nextIter = rbTreeNext(iter);
2168 rbTreeDeleteNode((rbTreeNode *)theArray->val.arrayPtr,
2169 iter, arrayDisposeNode);
2171 iter = nextIter;
2177 ** returns the number of elements (nodes containing values) of an array
2179 int ArraySize(DataValue *theArray)
2181 if (theArray->val.arrayPtr) {
2182 return(rbTreeSize((rbTreeNode *)theArray->val.arrayPtr));
2184 else {
2185 return(0);
2190 ** retrieves an array node whose key matches
2191 ** returns 1 for success 0 for not found
2193 int ArrayGet(DataValue *theArray, char *keyStr, DataValue *theValue)
2195 SparseArrayEntry searchEntry;
2196 rbTreeNode *foundNode;
2198 if (theArray->val.arrayPtr) {
2199 searchEntry.key = keyStr;
2200 foundNode = rbTreeFind((rbTreeNode *)theArray->val.arrayPtr,
2201 (rbTreeNode *)&searchEntry, arrayEntryCompare);
2202 if (foundNode) {
2203 *theValue = ((SparseArrayEntry *)foundNode)->value;
2204 return(1);
2207 return(0);
2211 ** get pointer to start iterating an array
2213 SparseArrayEntry *arrayIterateFirst(DataValue *theArray)
2215 SparseArrayEntry *startPos;
2216 if (theArray->val.arrayPtr) {
2217 startPos = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
2219 else {
2220 startPos = NULL;
2222 return(startPos);
2226 ** move iterator to next entry in array
2228 SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator)
2230 SparseArrayEntry *nextPos;
2231 if (iterator) {
2232 nextPos = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)iterator);
2234 else {
2235 nextPos = NULL;
2237 return(nextPos);
2241 ** evaluate an array element and push the result onto the stack
2243 ** Before: Prog-> [nDim], next, ...
2244 ** Stack-> indnDim, ... ind1, ArraySym, next, ...
2245 ** After: Prog-> nDim, [next], ...
2246 ** Stack-> indexedArrayVal, next, ...
2248 static int arrayRef(void)
2250 int errNum;
2251 DataValue srcArray, valueItem;
2252 char *keyString = NULL;
2253 int nDim;
2255 nDim = (int)*PC;
2256 PC++;
2258 DISASM_RT(PC-2, 2);
2259 STACKDUMP(nDim, 3);
2261 if (nDim > 0) {
2262 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2263 if (errNum != STAT_OK) {
2264 return(errNum);
2267 POP(srcArray)
2268 if (srcArray.tag == ARRAY_TAG) {
2269 if (!ArrayGet(&srcArray, keyString, &valueItem)) {
2270 return(execError("referenced array value not in array: %s", keyString));
2272 PUSH(valueItem)
2273 return(STAT_OK);
2275 else {
2276 return(execError("operator [] on non-array", NULL));
2279 else {
2280 POP(srcArray)
2281 if (srcArray.tag == ARRAY_TAG) {
2282 PUSH_INT(ArraySize(&srcArray))
2283 return(STAT_OK);
2285 else {
2286 return(execError("operator [] on non-array", NULL));
2292 ** assign to an array element of a referenced array on the stack
2294 ** Before: Prog-> [nDim], next, ...
2295 ** Stack-> rhs, indnDim, ... ind1, ArraySym, next, ...
2296 ** After: Prog-> nDim, [next], ...
2297 ** Stack-> next, ...
2299 static int arrayAssign(void)
2301 char *keyString = NULL;
2302 DataValue srcValue, dstArray;
2303 int errNum;
2304 int nDim;
2306 nDim = (int)*PC;
2307 PC++;
2309 DISASM_RT(PC-2, 1);
2310 STACKDUMP(nDim, 3);
2312 if (nDim > 0) {
2313 POP(srcValue)
2315 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2316 if (errNum != STAT_OK) {
2317 return(errNum);
2320 POP(dstArray)
2322 if (dstArray.tag != ARRAY_TAG && dstArray.tag != NO_TAG) {
2323 return(execError("cannot assign array element of non-array", NULL));
2325 if (srcValue.tag == ARRAY_TAG) {
2326 DataValue arrayCopyValue;
2328 errNum = ArrayCopy(&arrayCopyValue, &srcValue);
2329 srcValue = arrayCopyValue;
2330 if (errNum != STAT_OK) {
2331 return(errNum);
2334 if (ArrayInsert(&dstArray, keyString, &srcValue)) {
2335 return(STAT_OK);
2337 else {
2338 return(execError("array member allocation failure", NULL));
2341 return(execError("empty operator []", NULL));
2345 ** for use with assign-op operators (eg a[i,j] += k
2347 ** Before: Prog-> [binOp], nDim, next, ...
2348 ** Stack-> [rhs], indnDim, ... ind1, next, ...
2349 ** After: Prog-> binOp, nDim, [next], ...
2350 ** Stack-> [rhs], arrayValue, next, ...
2352 static int arrayRefAndAssignSetup(void)
2354 int errNum;
2355 DataValue srcArray, valueItem, moveExpr;
2356 char *keyString = NULL;
2357 int binaryOp, nDim;
2359 binaryOp = (int)*PC;
2360 PC++;
2361 nDim = (int)*PC;
2362 PC++;
2364 DISASM_RT(PC-3, 3);
2365 STACKDUMP(nDim + 1, 3);
2367 if (binaryOp) {
2368 POP(moveExpr)
2371 if (nDim > 0) {
2372 errNum = makeArrayKeyFromArgs(nDim, &keyString, 1);
2373 if (errNum != STAT_OK) {
2374 return(errNum);
2377 PEEK(srcArray, nDim)
2378 if (srcArray.tag == ARRAY_TAG) {
2379 if (!ArrayGet(&srcArray, keyString, &valueItem)) {
2380 return(execError("referenced array value not in array: %s", keyString));
2382 PUSH(valueItem)
2383 if (binaryOp) {
2384 PUSH(moveExpr)
2386 return(STAT_OK);
2388 else {
2389 return(execError("operator [] on non-array", NULL));
2392 else {
2393 return(execError("array[] not an lvalue", NULL));
2398 ** setup symbol values for array iteration in interpreter
2400 ** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ...
2401 ** Stack-> [arrayVal], next, ...
2402 ** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ...
2403 ** Stack-> [next], ...
2404 ** Where:
2405 ** iter is a symbol which gives the position of the iterator value in
2406 ** the stack frame
2407 ** arrayVal is the data value holding the array in question
2409 static int beginArrayIter(void)
2411 Symbol *iterator;
2412 DataValue *iteratorValPtr;
2413 DataValue arrayVal;
2415 DISASM_RT(PC-1, 2);
2416 STACKDUMP(1, 3);
2418 iterator = (Symbol *)*PC;
2419 PC++;
2421 POP(arrayVal)
2423 if (iterator->type == LOCAL_SYM) {
2424 iteratorValPtr = (FrameP + iterator->value.val.n);
2426 else {
2427 return(execError("bad temporary iterator: %s", iterator->name));
2430 iteratorValPtr->tag = INT_TAG;
2431 if (arrayVal.tag != ARRAY_TAG) {
2432 return(execError("can't iterate non-array", NULL));
2435 iteratorValPtr->val.arrayPtr = (struct SparseArrayEntry *)arrayIterateFirst(&arrayVal);
2436 return(STAT_OK);
2440 ** copy key to symbol if node is still valid, marked bad by a color of -1
2441 ** then move iterator to next node
2442 ** this allows iterators to progress even if you delete any node in the array
2443 ** except the item just after the current key
2445 ** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ...
2446 ** Stack-> [next], ...
2447 ** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ...
2448 ** Stack-> [next], ... (unchanged)
2449 ** Where:
2450 ** iter is a symbol which gives the position of the iterator value in
2451 ** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers
2452 ** to the array and a position within it
2453 ** iterVal is the programmer-visible symbol which will take the current
2454 ** key value
2455 ** endLoopBranch is the instruction offset to the instruction following the
2456 ** loop (measured from itself)
2457 ** arrayVal is the data value holding the array in question
2458 ** The return-to-start-of-loop branch (at the end of the loop) should address
2459 ** the ARRAY_ITER instruction
2461 static int arrayIter(void)
2463 Symbol *iterator;
2464 Symbol *item;
2465 DataValue *iteratorValPtr;
2466 DataValue *itemValPtr;
2467 SparseArrayEntry *thisEntry;
2468 Inst *branchAddr;
2470 DISASM_RT(PC-1, 4);
2471 STACKDUMP(0, 3);
2473 item = (Symbol *)*PC;
2474 PC++;
2475 iterator = (Symbol *)*PC;
2476 PC++;
2477 branchAddr = PC + (int)*PC;
2478 PC++;
2480 if (item->type == LOCAL_SYM) {
2481 itemValPtr = (FrameP + item->value.val.n);
2483 else if (item->type == GLOBAL_SYM) {
2484 itemValPtr = &(item->value);
2486 else {
2487 return(execError("can't assign to: %s", item->name));
2489 itemValPtr->tag = NO_TAG;
2491 if (iterator->type == LOCAL_SYM) {
2492 iteratorValPtr = (FrameP + iterator->value.val.n);
2494 else {
2495 return(execError("bad temporary iterator: %s", iterator->name));
2498 thisEntry = (SparseArrayEntry *)iteratorValPtr->val.arrayPtr;
2499 if (thisEntry && thisEntry->nodePtrs.color != -1) {
2500 itemValPtr->tag = STRING_TAG;
2501 itemValPtr->val.str = thisEntry->key;
2503 iteratorValPtr->val.arrayPtr = (struct SparseArrayEntry *)arrayIterateNext(thisEntry);
2505 else {
2506 PC = branchAddr;
2508 return(STAT_OK);
2512 ** determine if a key or keys exists in an array
2513 ** if the left argument is a string or integer a single check is performed
2514 ** if the key exists, 1 is pushed onto the stack, otherwise 0
2515 ** if the left argument is an array 1 is pushed onto the stack if every key
2516 ** in the left array exists in the right array, otherwise 0
2518 ** Before: Prog-> [next], ...
2519 ** Stack-> [ArraySym], inSymbol, next, ...
2520 ** After: Prog-> [next], ... -- (unchanged)
2521 ** Stack-> next, ...
2523 static int inArray(void)
2525 DataValue theArray, leftArray, theValue;
2526 char *keyStr;
2527 int inResult = 0;
2529 DISASM_RT(PC-1, 1);
2530 STACKDUMP(2, 3);
2532 POP(theArray)
2533 if (theArray.tag != ARRAY_TAG) {
2534 return(execError("operator in on non-array", NULL));
2536 PEEK(leftArray, 0)
2537 if (leftArray.tag == ARRAY_TAG) {
2538 SparseArrayEntry *iter;
2540 POP(leftArray)
2541 inResult = 1;
2542 iter = arrayIterateFirst(&leftArray);
2543 while (inResult && iter) {
2544 inResult = inResult && ArrayGet(&theArray, iter->key, &theValue);
2545 iter = arrayIterateNext(iter);
2548 else {
2549 POP_STRING(keyStr)
2550 if (ArrayGet(&theArray, keyStr, &theValue)) {
2551 inResult = 1;
2554 PUSH_INT(inResult)
2555 return(STAT_OK);
2559 ** remove a given key from an array unless nDim is 0, then all keys are removed
2561 ** for use with assign-op operators (eg a[i,j] += k
2562 ** Before: Prog-> [nDim], next, ...
2563 ** Stack-> [indnDim], ... ind1, arrayValue, next, ...
2564 ** After: Prog-> nDim, [next], ...
2565 ** Stack-> next, ...
2567 static int deleteArrayElement(void)
2569 DataValue theArray;
2570 char *keyString = NULL;
2571 int nDim;
2573 nDim = (int)*PC;
2574 PC++;
2576 DISASM_RT(PC-2, 2);
2577 STACKDUMP(nDim + 1, 3);
2579 if (nDim > 0) {
2580 int errNum;
2582 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2583 if (errNum != STAT_OK) {
2584 return(errNum);
2588 POP(theArray)
2589 if (theArray.tag == ARRAY_TAG) {
2590 if (nDim > 0) {
2591 ArrayDelete(&theArray, keyString);
2593 else {
2594 ArrayDeleteAll(&theArray);
2597 else {
2598 return(execError("attempt to delete from non-array", NULL));
2600 return(STAT_OK);
2604 ** checks errno after operations which can set it. If an error occured,
2605 ** creates appropriate error messages and returns false
2607 static int errCheck(const char *s)
2609 if (errno == EDOM)
2610 return execError("%s argument out of domain", s);
2611 else if (errno == ERANGE)
2612 return execError("%s result out of range", s);
2613 else
2614 return STAT_OK;
2618 ** combine two strings in a static area and set ErrMsg to point to the
2619 ** result. Returns false so a single return execError() statement can
2620 ** be used to both process the message and return.
2622 static int execError(const char *s1, const char *s2)
2624 static char msg[MAX_ERR_MSG_LEN];
2626 sprintf(msg, s1, s2);
2627 ErrMsg = msg;
2628 return STAT_ERROR;
2631 int StringToNum(const char *string, int *number)
2633 const char *c = string;
2635 while (*c == ' ' || *c == '\t') {
2636 ++c;
2638 if (*c == '+' || *c == '-') {
2639 ++c;
2641 while (isdigit((unsigned char)*c)) {
2642 ++c;
2644 while (*c == ' ' || *c == '\t') {
2645 ++c;
2647 if (*c) {
2648 /* if everything went as expected, we should be at end, but we're not */
2649 return False;
2651 if (number) {
2652 if (sscanf(string, "%d", number) != 1) {
2653 /* This case is here to support old behavior */
2654 *number = 0;
2657 return True;
2660 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2661 static void dumpVal(DataValue dv)
2663 switch (dv.tag) {
2664 case INT_TAG:
2665 printf("i=%d", dv.val.n);
2666 break;
2667 case STRING_TAG:
2669 int k;
2670 char s[21];
2671 char *src = dv.val.str;
2672 if (!src) {
2673 printf("s=<NULL>");
2675 else {
2676 for (k = 0; src[k] && k < sizeof s - 1; k++) {
2677 s[k] = isprint(src[k]) ? src[k] : '?';
2679 s[k] = 0;
2680 printf("s=\"%s\"%s[%d]", s,
2681 src[k] ? "..." : "", strlen(src));
2684 break;
2685 case ARRAY_TAG:
2686 printf("<array>");
2687 break;
2688 case NO_TAG:
2689 if (!dv.val.inst) {
2690 printf("<no value>");
2692 else {
2693 printf("?%8p", dv.val.inst);
2695 break;
2696 default:
2697 printf("UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst);
2698 break;
2701 #endif /* #ifdef DEBUG_DISASSEMBLER */
2703 #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */
2704 static void disasm(Inst *inst, int nInstr)
2706 static const char *opNames[N_OPS] = {
2707 "RETURN_NO_VAL", /* returnNoVal */
2708 "RETURN", /* returnVal */
2709 "PUSH_SYM", /* pushSymVal */
2710 "DUP", /* dupStack */
2711 "ADD", /* add */
2712 "SUB", /* subtract */
2713 "MUL", /* multiply */
2714 "DIV", /* divide */
2715 "MOD", /* modulo */
2716 "NEGATE", /* negate */
2717 "INCR", /* increment */
2718 "DECR", /* decrement */
2719 "GT", /* gt */
2720 "LT", /* lt */
2721 "GE", /* ge */
2722 "LE", /* le */
2723 "EQ", /* eq */
2724 "NE", /* ne */
2725 "BIT_AND", /* bitAnd */
2726 "BIT_OR", /* bitOr */
2727 "AND", /* and */
2728 "OR", /* or */
2729 "NOT", /* not */
2730 "POWER", /* power */
2731 "CONCAT", /* concat */
2732 "ASSIGN", /* assign */
2733 "SUBR_CALL", /* callSubroutine */
2734 "FETCH_RET_VAL", /* fetchRetVal */
2735 "BRANCH", /* branch */
2736 "BRANCH_TRUE", /* branchTrue */
2737 "BRANCH_FALSE", /* branchFalse */
2738 "BRANCH_NEVER", /* branchNever */
2739 "ARRAY_REF", /* arrayRef */
2740 "ARRAY_ASSIGN", /* arrayAssign */
2741 "BEGIN_ARRAY_ITER", /* beginArrayIter */
2742 "ARRAY_ITER", /* arrayIter */
2743 "IN_ARRAY", /* inArray */
2744 "ARRAY_DELETE", /* deleteArrayElement */
2745 "PUSH_ARRAY_SYM", /* pushArraySymVal */
2746 "ARRAY_REF_ASSIGN_SETUP" /* arrayRefAndAssignSetup */
2748 int i, j;
2750 printf("\n");
2751 for (i=0; i<nInstr; i++) {
2752 printf("Prog %8p ", &inst[i]);
2753 for (j=0; j<N_OPS; j++) {
2754 if (inst[i] == OpFns[j]) {
2755 printf("%22s ", opNames[j]);
2756 if (j == OP_PUSH_SYM || j == OP_ASSIGN) {
2757 Symbol *sym = (Symbol *)inst[i+1];
2758 printf("%s", sym->name);
2759 if (sym->value.tag == STRING_TAG &&
2760 strncmp(sym->name, "string #", 8) == 0) {
2761 dumpVal(sym->value);
2763 ++i;
2764 } else if (j == OP_BRANCH || j == OP_BRANCH_FALSE ||
2765 j == OP_BRANCH_NEVER || j == OP_BRANCH_TRUE) {
2766 printf("to=(%d) %x", (int)inst[i+1],
2767 (int)(&inst[i+1] + (int)inst[i+1]));
2768 ++i;
2769 } else if (j == OP_SUBR_CALL) {
2770 printf("%s (%d arg)", ((Symbol *)inst[i+1])->name,
2771 (int)inst[i+2]);
2772 i += 2;
2773 } else if (j == OP_BEGIN_ARRAY_ITER) {
2774 printf("%s in",
2775 ((Symbol *)inst[i+1])->name);
2776 ++i;
2777 } else if (j == OP_ARRAY_ITER) {
2778 printf("%s = %s++ end-loop=(%d) %x",
2779 ((Symbol *)inst[i+1])->name,
2780 ((Symbol *)inst[i+2])->name,
2781 (int)inst[i+3],
2782 (int)(&inst[i+3] + (int)inst[i+3]));
2783 i += 3;
2784 } else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
2785 j == OP_ARRAY_ASSIGN) {
2786 printf("nDim=%d",
2787 ((int)inst[i+1]));
2788 ++i;
2789 } else if (j == OP_ARRAY_REF_ASSIGN_SETUP) {
2790 printf("binOp=%s ",
2791 ((int)inst[i+1]) ? "true" : "false");
2792 printf("nDim=%d",
2793 ((int)inst[i+2]));
2794 i += 2;
2795 } else if (j == OP_PUSH_ARRAY_SYM) {
2796 printf("%s", ((Symbol *)inst[++i])->name);
2797 printf(" %s",
2798 (int)inst[i+1] ? "createAndRef" : "refOnly");
2799 ++i;
2802 printf("\n");
2803 break;
2806 if (j == N_OPS)
2807 printf("%x\n", (int)inst[i]);
2810 #endif /* #ifdef DEBUG_DISASSEMBLER */
2812 #ifdef DEBUG_STACK /* for run-time stack dumping */
2813 static void stackdump(int n, int extra)
2815 /* Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argN-arg1, next, ... */
2816 int nArgs = FrameP[-1].val.n;
2817 int i, offset;
2818 char buffer[20];
2819 printf("Stack ----->\n");
2820 for (i = 0; i < n + extra; i++) {
2821 char *pos = "";
2822 DataValue *dv = &StackP[-i - 1];
2823 if (dv < Stack) {
2824 printf("--------------Stack base--------------\n");
2825 break;
2827 offset = dv - FrameP;
2829 printf("%4.4s", i < n ? ">>>>" : "");
2830 printf("%8p ", dv);
2831 switch (offset) {
2832 case 0: pos = "FrameP"; break; /* first local symbol value */
2833 case -1: pos = "NArgs"; break; /* number of arguments */
2834 case -2: pos = "OldFP"; break;
2835 case -3: pos = "RetPC"; break;
2836 default:
2837 if (offset < -3 && offset >= -3 - nArgs) {
2838 sprintf(pos = buffer, "Arg%d", offset + 4 + nArgs);
2840 break;
2842 printf("%-6s ", pos);
2843 dumpVal(*dv);
2844 printf("\n");
2847 #endif /* ifdef DEBUG_STACK */