Fix possible stack overflows in interpret.c
[nedit.git] / source / interpret.c
blob431d437734c41f2ad8d7edcf91ae001b994e5deb
1 static const char CVSID[] = "$Id: interpret.c,v 1.49 2008/03/01 00:27:32 lebert 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. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * April, 1997 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "interpret.h"
35 #include "textBuf.h"
36 #include "nedit.h"
37 #include "menu.h"
38 #include "text.h"
39 #include "rbTree.h"
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <math.h>
45 #include <limits.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #ifdef VMS
49 #include "../util/VMSparam.h"
50 #else
51 #ifndef __MVS__
52 #include <sys/param.h>
53 #endif
54 #endif /*VMS*/
56 #include <X11/Intrinsic.h>
57 #include <Xm/Xm.h>
59 #include "window.h"
61 #ifdef HAVE_DEBUG_H
62 #include "../debug.h"
63 #endif
65 #define PROGRAM_SIZE 4096 /* Maximum program size */
66 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
67 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
68 allowed per program */
69 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
70 allowed to execute before preempting and
71 returning to allow other things to run */
73 /* Temporary markers placed in a branch address location to designate
74 which loop address (break or continue) the location needs */
75 #define NEEDS_BREAK 1
76 #define NEEDS_CONTINUE 2
78 #define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */
80 enum opStatusCodes {STAT_OK=2, STAT_DONE, STAT_ERROR, STAT_PREEMPT};
82 static void addLoopAddr(Inst *addr);
83 static void saveContext(RestartData *context);
84 static void restoreContext(RestartData *context);
85 static int returnNoVal(void);
86 static int returnVal(void);
87 static int returnValOrNone(int valOnStack);
88 static int pushSymVal(void);
89 static int pushArgVal(void);
90 static int pushArgCount(void);
91 static int pushArgArray(void);
92 static int pushArraySymVal(void);
93 static int dupStack(void);
94 static int add(void);
95 static int subtract(void);
96 static int multiply(void);
97 static int divide(void);
98 static int modulo(void);
99 static int negate(void);
100 static int increment(void);
101 static int decrement(void);
102 static int gt(void);
103 static int lt(void);
104 static int ge(void);
105 static int le(void);
106 static int eq(void);
107 static int ne(void);
108 static int bitAnd(void);
109 static int bitOr(void);
110 static int and(void);
111 static int or(void);
112 static int not(void);
113 static int power(void);
114 static int concat(void);
115 static int assign(void);
116 static int callSubroutine(void);
117 static int fetchRetVal(void);
118 static int branch(void);
119 static int branchTrue(void);
120 static int branchFalse(void);
121 static int branchNever(void);
122 static int arrayRef(void);
123 static int arrayAssign(void);
124 static int arrayRefAndAssignSetup(void);
125 static int beginArrayIter(void);
126 static int arrayIter(void);
127 static int inArray(void);
128 static int deleteArrayElement(void);
129 static void freeSymbolTable(Symbol *symTab);
130 static int errCheck(const char *s);
131 static int execError(const char *s1, const char *s2);
132 static rbTreeNode *arrayEmptyAllocator(void);
133 static rbTreeNode *arrayAllocateNode(rbTreeNode *src);
134 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src);
135 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right);
136 static void arrayDisposeNode(rbTreeNode *src);
137 static SparseArrayEntry *allocateSparseArrayEntry(void);
139 /*#define DEBUG_ASSEMBLY*/
140 /*#define DEBUG_STACK*/
142 #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK)
143 #define DEBUG_DISASSEMBLER
144 static void disasm(Inst *inst, int nInstr);
145 #endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */
147 #ifdef DEBUG_ASSEMBLY /* for disassembly */
148 #define DISASM(i, n) disasm(i, n)
149 #else /* #ifndef DEBUG_ASSEMBLY */
150 #define DISASM(i, n)
151 #endif /* #ifndef DEBUG_ASSEMBLY */
153 #ifdef DEBUG_STACK /* for run-time instruction and stack trace */
154 static void stackdump(int n, int extra);
155 #define STACKDUMP(n, x) stackdump(n, x)
156 #define DISASM_RT(i, n) disasm(i, n)
157 #else /* #ifndef DEBUG_STACK */
158 #define STACKDUMP(n, x)
159 #define DISASM_RT(i, n)
160 #endif /* #ifndef DEBUG_STACK */
162 /* Global symbols and function definitions */
163 static Symbol *GlobalSymList = NULL;
165 /* List of all memory allocated for strings */
166 static char *AllocatedStrings = NULL;
168 typedef struct {
169 SparseArrayEntry data; /* LEAVE this as top entry */
170 int inUse; /* we use pointers to the data to refer to the entire struct */
171 struct SparseArrayEntryWrapper *next;
172 } SparseArrayEntryWrapper;
174 static SparseArrayEntryWrapper *AllocatedSparseArrayEntries = NULL;
176 /* Message strings used in macros (so they don't get repeated every time
177 the macros are used */
178 static const char *StackOverflowMsg = "macro stack overflow";
179 static const char *StackUnderflowMsg = "macro stack underflow";
180 static const char *StringToNumberMsg = "string could not be converted to number";
182 /* Temporary global data for use while accumulating programs */
183 static Symbol *LocalSymList = NULL; /* symbols local to the program */
184 static Inst Prog[PROGRAM_SIZE]; /* the program */
185 static Inst *ProgP; /* next free spot for code gen. */
186 static Inst *LoopStack[LOOP_STACK_SIZE]; /* addresses of break, cont stmts */
187 static Inst **LoopStackPtr = LoopStack; /* to fill at the end of a loop */
189 /* Global data for the interpreter */
190 static DataValue *TheStack; /* the stack */
191 static DataValue *StackP; /* next free spot on stack */
192 static DataValue *FrameP; /* frame pointer (start of local variables
193 for the current subroutine invocation) */
194 static Inst *PC; /* program counter during execution */
195 static char *ErrMsg; /* global for returning error messages
196 from executing functions */
197 static WindowInfo
198 *InitiatingWindow = NULL; /* window from which macro was run */
199 static WindowInfo *FocusWindow; /* window on which macro commands operate */
200 static int PreemptRequest; /* passes preemption requests from called
201 routines back up to the interpreter */
203 /* Array for mapping operations to functions for performing the operations
204 Must correspond to the enum called "operations" in interpret.h */
205 static int (*OpFns[N_OPS])() = {returnNoVal, returnVal, pushSymVal, dupStack,
206 add, subtract, multiply, divide, modulo, negate, increment, decrement,
207 gt, lt, ge, le, eq, ne, bitAnd, bitOr, and, or, not, power, concat,
208 assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse,
209 branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
210 deleteArrayElement, pushArraySymVal,
211 arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray};
213 /* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
214 #define FP_ARG_ARRAY_CACHE_INDEX (-1)
215 #define FP_ARG_COUNT_INDEX (-2)
216 #define FP_OLD_FP_INDEX (-3)
217 #define FP_RET_PC_INDEX (-4)
218 #define FP_TO_ARGS_DIST (4) /* should be 0 - (above index) */
219 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
220 #define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_CACHE_INDEX))
221 #define FP_GET_ARG_COUNT(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_COUNT_INDEX).val.n)
222 #define FP_GET_OLD_FP(xFrameP) ((FP_GET_ITEM(xFrameP, FP_OLD_FP_INDEX)).val.dataval)
223 #define FP_GET_RET_PC(xFrameP) ((FP_GET_ITEM(xFrameP, FP_RET_PC_INDEX)).val.inst)
224 #define FP_ARG_START_INDEX(xFrameP) (-(FP_GET_ARG_COUNT(xFrameP) + FP_TO_ARGS_DIST))
225 #define FP_GET_ARG_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN + FP_ARG_START_INDEX(xFrameP)))
226 #define FP_GET_SYM_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN))
227 #define FP_GET_SYM_VAL(xFrameP,xSym) (FP_GET_SYM_N(xFrameP, xSym->value.val.n))
230 ** Initialize macro language global variables. Must be called before
231 ** any macros are even parsed, because the parser uses action routine
232 ** symbols to comprehend hyphenated names.
234 void InitMacroGlobals(void)
236 XtActionsRec *actions;
237 int i, nActions;
238 static char argName[3] = "$x";
239 static DataValue dv = {NO_TAG, {0}};
241 /* Add action routines from NEdit menus and text widget */
242 actions = GetMenuActions(&nActions);
243 for (i=0; i<nActions; i++) {
244 dv.val.xtproc = actions[i].proc;
245 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv);
247 actions = TextGetActions(&nActions);
248 for (i=0; i<nActions; i++) {
249 dv.val.xtproc = actions[i].proc;
250 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv);
253 /* Add subroutine argument symbols ($1, $2, ..., $9) */
254 for (i=0; i<9; i++) {
255 argName[1] = '1' + i;
256 dv.val.n = i;
257 InstallSymbol(argName, ARG_SYM, dv);
260 /* Add special symbol $n_args */
261 dv.val.n = N_ARGS_ARG_SYM;
262 InstallSymbol("$n_args", ARG_SYM, dv);
266 ** To build a program for the interpreter, call BeginCreatingProgram, to
267 ** begin accumulating the program, followed by calls to AddOp, AddSym,
268 ** and InstallSymbol to add symbols and operations. When the new program
269 ** is finished, collect the results with FinishCreatingProgram. This returns
270 ** a self contained program that can be run with ExecuteMacro.
274 ** Start collecting instructions for a program. Clears the program
275 ** and the symbol table.
277 void BeginCreatingProgram(void)
279 LocalSymList = NULL;
280 ProgP = Prog;
281 LoopStackPtr = LoopStack;
285 ** Finish up the program under construction, and return it (code and
286 ** symbol table) as a package that ExecuteMacro can execute. This
287 ** program must be freed with FreeProgram.
289 Program *FinishCreatingProgram(void)
291 Program *newProg;
292 int progLen, fpOffset = 0;
293 Symbol *s;
295 newProg = (Program *)XtMalloc(sizeof(Program));
296 progLen = ((char *)ProgP) - ((char *)Prog);
297 newProg->code = (Inst *)XtMalloc(progLen);
298 memcpy(newProg->code, Prog, progLen);
299 newProg->localSymList = LocalSymList;
300 LocalSymList = NULL;
302 /* Local variables' values are stored on the stack. Here we assign
303 frame pointer offsets to them. */
304 for (s = newProg->localSymList; s != NULL; s = s->next)
305 s->value.val.n = fpOffset++;
307 DISASM(newProg->code, ProgP - Prog);
309 return newProg;
312 void FreeProgram(Program *prog)
314 freeSymbolTable(prog->localSymList);
315 XtFree((char *)prog->code);
316 XtFree((char *)prog);
320 ** Add an operator (instruction) to the end of the current program
322 int AddOp(int op, char **msg)
324 if (ProgP >= &Prog[PROGRAM_SIZE]) {
325 *msg = "macro too large";
326 return 0;
328 ProgP->func = OpFns[op];
329 ProgP++;
330 return 1;
334 ** Add a symbol operand to the current program
336 int AddSym(Symbol *sym, char **msg)
338 if (ProgP >= &Prog[PROGRAM_SIZE]) {
339 *msg = "macro too large";
340 return 0;
342 ProgP->sym = sym;
343 ProgP++;
344 return 1;
348 ** Add an immediate value operand to the current program
350 int AddImmediate(int value, char **msg)
352 if (ProgP >= &Prog[PROGRAM_SIZE]) {
353 *msg = "macro too large";
354 return 0;
356 ProgP->value = value;
357 ProgP++;
358 return 1;
362 ** Add a branch offset operand to the current program
364 int AddBranchOffset(Inst *to, char **msg)
366 if (ProgP >= &Prog[PROGRAM_SIZE]) {
367 *msg = "macro too large";
368 return 0;
370 /* Should be ptrdiff_t for branch offsets */
371 ProgP->value = to - ProgP;
372 ProgP++;
374 return 1;
378 ** Return the address at which the next instruction will be stored
380 Inst *GetPC(void)
382 return ProgP;
386 ** Swap the positions of two contiguous blocks of code. The first block
387 ** running between locations start and boundary, and the second between
388 ** boundary and end.
390 void SwapCode(Inst *start, Inst *boundary, Inst *end)
392 #define reverseCode(L, H) \
393 do { register Inst t, *l = L, *h = H - 1; \
394 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
395 /* double-reverse method: reverse elements of both parts then whole lot */
396 /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
397 reverseCode(start, boundary); /* 1 */
398 reverseCode(boundary, end); /* 2 */
399 reverseCode(start, end); /* 3 */
403 ** Maintain a stack to save addresses of branch operations for break and
404 ** continue statements, so they can be filled in once the information
405 ** on where to branch is known.
407 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or
408 ** AddContinueAddr to register the address at which to store the branch
409 ** address for a break or continue statement, and FillLoopAddrs to fill
410 ** in all the addresses and return to the level of the enclosing loop.
412 void StartLoopAddrList(void)
414 addLoopAddr(NULL);
417 int AddBreakAddr(Inst *addr)
419 if (LoopStackPtr == LoopStack) return 1;
420 addLoopAddr(addr);
421 addr->value = NEEDS_BREAK;
422 return 0;
425 int AddContinueAddr(Inst *addr)
427 if (LoopStackPtr == LoopStack) return 1;
428 addLoopAddr(addr);
429 addr->value = NEEDS_CONTINUE;
430 return 0;
433 static void addLoopAddr(Inst *addr)
435 if (LoopStackPtr > &LoopStack[LOOP_STACK_SIZE-1]) {
436 fprintf(stderr, "NEdit: loop stack overflow in macro parser");
437 return;
439 *LoopStackPtr++ = addr;
442 void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr)
444 while (True) {
445 LoopStackPtr--;
446 if (LoopStackPtr < LoopStack) {
447 fprintf(stderr, "NEdit: internal error (lsu) in macro parser\n");
448 return;
450 if (*LoopStackPtr == NULL)
451 break;
452 if ((*LoopStackPtr)->value == NEEDS_BREAK)
453 **(Inst ***)LoopStackPtr = (Inst *)(breakAddr - *LoopStackPtr);
454 else if ((*LoopStackPtr)->value == NEEDS_CONTINUE)
455 **(Inst ***)LoopStackPtr = (Inst *)(continueAddr - *LoopStackPtr);
456 else
457 fprintf(stderr, "NEdit: internal error (uat) in macro parser\n");
462 ** Execute a compiled macro, "prog", using the arguments in the array
463 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.
464 ** if MACRO_DONE is returned, the macro completed, and the returned value
465 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the
466 ** macro exceeded its alotted time-slice and scheduled...
468 int ExecuteMacro(WindowInfo *window, Program *prog, int nArgs, DataValue *args,
469 DataValue *result, RestartData **continuation, char **msg)
471 RestartData *context;
472 static DataValue noValue = {NO_TAG, {0}};
473 Symbol *s;
474 int i;
476 /* Create an execution context (a stack, a stack pointer, a frame pointer,
477 and a program counter) which will retain the program state across
478 preemption and resumption of execution */
479 context = (RestartData *)XtMalloc(sizeof(RestartData));
480 context->stack = (DataValue *)XtMalloc(sizeof(DataValue) * STACK_SIZE);
481 *continuation = context;
482 context->stackP = context->stack;
483 context->pc = prog->code;
484 context->runWindow = window;
485 context->focusWindow = window;
487 /* Push arguments and call information onto the stack */
488 for (i=0; i<nArgs; i++)
489 *(context->stackP++) = args[i];
491 context->stackP->val.subr = NULL; /* return PC */
492 context->stackP->tag = NO_TAG;
493 context->stackP++;
495 *(context->stackP++) = noValue; /* old FrameP */
497 context->stackP->tag = NO_TAG; /* nArgs */
498 context->stackP->val.n = nArgs;
499 context->stackP++;
501 *(context->stackP++) = noValue; /* cached arg array */
503 context->frameP = context->stackP;
505 /* Initialize and make room on the stack for local variables */
506 for (s = prog->localSymList; s != NULL; s = s->next) {
507 FP_GET_SYM_VAL(context->frameP, s) = noValue;
508 context->stackP++;
511 /* Begin execution, return on error or preemption */
512 return ContinueMacro(context, result, msg);
516 ** Continue the execution of a suspended macro whose state is described in
517 ** "continuation"
519 int ContinueMacro(RestartData *continuation, DataValue *result, char **msg)
521 register int status, instCount = 0;
522 register Inst *inst;
523 RestartData oldContext;
525 /* To allow macros to be invoked arbitrarily (such as those automatically
526 triggered within smart-indent) within executing macros, this call is
527 reentrant. */
528 saveContext(&oldContext);
531 ** Execution Loop: Call the succesive routine addresses in the program
532 ** until one returns something other than STAT_OK, then take action
534 restoreContext(continuation);
535 ErrMsg = NULL;
536 for (;;) {
538 /* Execute an instruction */
539 inst = PC++;
540 status = (inst->func)();
542 /* If error return was not STAT_OK, return to caller */
543 if (status != STAT_OK) {
544 if (status == STAT_PREEMPT) {
545 saveContext(continuation);
546 restoreContext(&oldContext);
547 return MACRO_PREEMPT;
548 } else if (status == STAT_ERROR) {
549 *msg = ErrMsg;
550 FreeRestartData(continuation);
551 restoreContext(&oldContext);
552 return MACRO_ERROR;
553 } else if (status == STAT_DONE) {
554 *msg = "";
555 *result = *--StackP;
556 FreeRestartData(continuation);
557 restoreContext(&oldContext);
558 return MACRO_DONE;
562 /* Count instructions executed. If the instruction limit is hit,
563 preempt, store re-start information in continuation and give
564 X, other macros, and other shell scripts a chance to execute */
565 instCount++;
566 if (instCount >= INSTRUCTION_LIMIT) {
567 saveContext(continuation);
568 restoreContext(&oldContext);
569 return MACRO_TIME_LIMIT;
575 ** If a macro is already executing, and requests that another macro be run,
576 ** this can be called instead of ExecuteMacro to run it in the same context
577 ** as if it were a subroutine. This saves the caller from maintaining
578 ** separate contexts, and serializes processing of the two macros without
579 ** additional work.
581 void RunMacroAsSubrCall(Program *prog)
583 Symbol *s;
584 static DataValue noValue = {NO_TAG, {0}};
586 /* See subroutine "callSubroutine" for a description of the stack frame
587 for a subroutine call */
588 StackP->tag = NO_TAG;
589 StackP->val.inst = PC; /* return PC */
590 StackP++;
592 StackP->tag = NO_TAG;
593 StackP->val.dataval = FrameP; /* old FrameP */
594 StackP++;
596 StackP->tag = NO_TAG; /* nArgs */
597 StackP->val.n = 0;
598 StackP++;
600 *(StackP++) = noValue; /* cached arg array */
602 FrameP = StackP;
603 PC = prog->code;
604 for (s = prog->localSymList; s != NULL; s = s->next) {
605 FP_GET_SYM_VAL(FrameP, s) = noValue;
606 StackP++;
610 void FreeRestartData(RestartData *context)
612 XtFree((char *)context->stack);
613 XtFree((char *)context);
617 ** Cause a macro in progress to be preempted (called by commands which take
618 ** a long time, or want to return to the event loop. Call ResumeMacroExecution
619 ** to resume.
621 void PreemptMacro(void)
623 PreemptRequest = True;
627 ** Reset the return value for a subroutine which caused preemption (this is
628 ** how to return a value from a routine which preempts instead of returning
629 ** a value directly).
631 void ModifyReturnedValue(RestartData *context, DataValue dv)
633 if ((context->pc-1)->func == fetchRetVal)
634 *(context->stackP-1) = dv;
638 ** Called within a routine invoked from a macro, returns the window in
639 ** which the macro is executing (where the banner is, not where it is focused)
641 WindowInfo *MacroRunWindow(void)
643 return InitiatingWindow;
647 ** Called within a routine invoked from a macro, returns the window to which
648 ** the currently executing macro is focused (the window which macro commands
649 ** modify, not the window from which the macro is being run)
651 WindowInfo *MacroFocusWindow(void)
653 return FocusWindow;
657 ** Set the window to which macro subroutines and actions which operate on an
658 ** implied window are directed.
660 void SetMacroFocusWindow(WindowInfo *window)
662 FocusWindow = window;
666 ** install an array iteration symbol
667 ** it is tagged as an integer but holds an array node pointer
669 #define ARRAY_ITER_SYM_PREFIX "aryiter "
670 Symbol *InstallIteratorSymbol(void)
672 char symbolName[sizeof(ARRAY_ITER_SYM_PREFIX) + TYPE_INT_STR_SIZE(int)];
673 DataValue value;
674 static int interatorNameIndex = 0;
676 sprintf(symbolName, ARRAY_ITER_SYM_PREFIX "#%d", interatorNameIndex);
677 ++interatorNameIndex;
678 value.tag = INT_TAG;
679 value.val.arrayPtr = NULL;
680 return(InstallSymbol(symbolName, LOCAL_SYM, value));
684 ** Lookup a constant string by its value. This allows reuse of string
685 ** constants and fixing a leak in the interpreter.
687 Symbol *LookupStringConstSymbol(const char *value)
689 Symbol *s;
691 for (s = GlobalSymList; s != NULL; s = s->next) {
692 if (s->type == CONST_SYM &&
693 s->value.tag == STRING_TAG &&
694 !strcmp(s->value.val.str.rep, value)) {
695 return(s);
698 return(NULL);
702 ** install string str in the global symbol table with a string name
704 Symbol *InstallStringConstSymbol(const char *str)
706 static int stringConstIndex = 0;
707 char stringName[35];
708 DataValue value;
709 Symbol *sym = LookupStringConstSymbol(str);
710 if (sym) {
711 return sym;
714 sprintf(stringName, "string #%d", stringConstIndex++);
715 value.tag = STRING_TAG;
716 AllocNStringCpy(&value.val.str, str);
717 return(InstallSymbol(stringName, CONST_SYM, value));
721 ** find a symbol in the symbol table
723 Symbol *LookupSymbol(const char *name)
725 Symbol *s;
727 for (s = LocalSymList; s != NULL; s = s->next)
728 if (strcmp(s->name, name) == 0)
729 return s;
730 for (s = GlobalSymList; s != NULL; s = s->next)
731 if (strcmp(s->name, name) == 0)
732 return s;
733 return NULL;
737 ** install symbol name in symbol table
739 Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value)
741 Symbol *s;
743 s = (Symbol *)malloc(sizeof(Symbol));
744 s->name = (char *)malloc(strlen(name)+1); /* +1 for '\0' */
745 strcpy(s->name, name);
746 s->type = type;
747 s->value = value;
748 if (type == LOCAL_SYM) {
749 s->next = LocalSymList;
750 LocalSymList = s;
751 } else {
752 s->next = GlobalSymList;
753 GlobalSymList = s;
755 return s;
759 ** Promote a symbol from local to global, removing it from the local symbol
760 ** list.
762 Symbol *PromoteToGlobal(Symbol *sym)
764 Symbol *s;
765 static DataValue noValue = {NO_TAG, {0}};
767 if (sym->type != LOCAL_SYM)
768 return sym;
770 /* Remove sym from the local symbol list */
771 if (sym == LocalSymList)
772 LocalSymList = sym->next;
773 else {
774 for (s = LocalSymList; s != NULL; s = s->next) {
775 if (s->next == sym) {
776 s->next = sym->next;
777 break;
782 s = LookupSymbol(sym->name);
783 if (s != NULL)
784 return s;
785 return InstallSymbol(sym->name, GLOBAL_SYM, noValue);
789 ** Allocate memory for a string, and keep track of it, such that it
790 ** can be recovered later using GarbageCollectStrings. (A linked list
791 ** of pointers is maintained by threading through the memory behind
792 ** the returned pointers). Length does not include the terminating null
793 ** character, so to allocate space for a string of strlen == n, you must
794 ** use AllocString(n+1).
797 /*#define TRACK_GARBAGE_LEAKS*/
798 #ifdef TRACK_GARBAGE_LEAKS
799 static int numAllocatedStrings = 0;
800 static int numAllocatedSparseArrayElements = 0;
801 #endif
803 /* Allocate a new string buffer of length chars */
804 char *AllocString(int length)
806 char *mem;
808 mem = XtMalloc(length + sizeof(char *) + 1);
809 *((char **)mem) = AllocatedStrings;
810 AllocatedStrings = mem;
811 #ifdef TRACK_GARBAGE_LEAKS
812 ++numAllocatedStrings;
813 #endif
814 return mem + sizeof(char *) + 1;
818 * Allocate a new NString buffer of length chars (terminating \0 included),
819 * The buffer length is initialized to length-1 and the terminating \0 is
820 * filled in.
822 int AllocNString(NString *string, int length)
824 char *mem;
826 mem = XtMalloc(length + sizeof(char *) + 1);
827 if (!mem) {
828 string->rep = 0;
829 string->len = 0;
830 return False;
833 *((char **)mem) = AllocatedStrings;
834 AllocatedStrings = mem;
835 #ifdef TRACK_GARBAGE_LEAKS
836 ++numAllocatedStrings;
837 #endif
838 string->rep = mem + sizeof(char *) + 1;
839 string->rep[length-1] = '\0'; /* forced \0 */
840 string->len = length-1;
841 return True;
844 /* Allocate a new string buffer of length chars, and copy in the string s */
845 char *AllocStringNCpy(const char *s, int length)
847 char *p = AllocString(length + 1); /* add extra char for forced \0 */
848 if (!p)
849 return p;
850 if (!s)
851 s = "";
852 p[length] = '\0'; /* forced \0 */
853 return strncpy(p, s, length);
857 * Allocate a new NString buffer of length chars (terminating \0 NOT included),
858 * and copy at most length characters of the given string.
859 * The buffer length is properly set and the buffer is guaranteed to be
860 * \0-terminated.
862 int AllocNStringNCpy(NString *string, const char *s, int length)
864 if (!AllocNString(string, length + 1)) /* add extra char for forced \0 */
865 return False;
866 if (!s)
867 s = "";
868 strncpy(string->rep, s, length);
869 string->len = strlen(string->rep); /* re-calculate! */
870 return True;
873 /* Allocate a new copy of string s */
874 char *AllocStringCpy(const char *s)
876 return AllocStringNCpy(s, s ? strlen(s) : 0);
880 * Allocate a new NString buffer, containing a copy of the given string.
881 * The length is set to the length of the string and resulting string is
882 * guaranteed to be \0-terminated.
884 int AllocNStringCpy(NString *string, const char *s)
886 size_t length = s ? strlen(s) : 0;
887 if (!AllocNString(string, length + 1))
888 return False;
889 if (s)
890 strncpy(string->rep, s, length);
891 return True;
894 static SparseArrayEntry *allocateSparseArrayEntry(void)
896 SparseArrayEntryWrapper *mem;
898 mem = (SparseArrayEntryWrapper *)XtMalloc(sizeof(SparseArrayEntryWrapper));
899 mem->next = (struct SparseArrayEntryWrapper *)AllocatedSparseArrayEntries;
900 AllocatedSparseArrayEntries = mem;
901 #ifdef TRACK_GARBAGE_LEAKS
902 ++numAllocatedSparseArrayElements;
903 #endif
904 return(&(mem->data));
907 static void MarkArrayContentsAsUsed(SparseArrayEntry *arrayPtr)
909 SparseArrayEntry *globalSEUse;
911 if (arrayPtr) {
912 ((SparseArrayEntryWrapper *)arrayPtr)->inUse = 1;
913 for (globalSEUse = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)arrayPtr);
914 globalSEUse != NULL;
915 globalSEUse = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)globalSEUse)) {
917 ((SparseArrayEntryWrapper *)globalSEUse)->inUse = 1;
918 /* test first because it may be read-only static string */
919 if (!(*(globalSEUse->key - 1))) {
920 *(globalSEUse->key - 1) = 1;
922 if (globalSEUse->value.tag == STRING_TAG) {
923 /* test first because it may be read-only static string */
924 if (!(*(globalSEUse->value.val.str.rep - 1))) {
925 *(globalSEUse->value.val.str.rep - 1) = 1;
928 else if (globalSEUse->value.tag == ARRAY_TAG) {
929 MarkArrayContentsAsUsed((SparseArrayEntry *)globalSEUse->value.val.arrayPtr);
936 ** Collect strings that are no longer referenced from the global symbol
937 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must
938 ** only be run after all macro activity has ceased.
941 void GarbageCollectStrings(void)
943 SparseArrayEntryWrapper *nextAP, *thisAP;
944 char *p, *next;
945 Symbol *s;
947 /* mark all strings as unreferenced */
948 for (p = AllocatedStrings; p != NULL; p = *((char **)p)) {
949 *(p + sizeof(char *)) = 0;
952 for (thisAP = AllocatedSparseArrayEntries;
953 thisAP != NULL; thisAP = (SparseArrayEntryWrapper *)thisAP->next) {
954 thisAP->inUse = 0;
957 /* Sweep the global symbol list, marking which strings are still
958 referenced */
959 for (s = GlobalSymList; s != NULL; s = s->next) {
960 if (s->value.tag == STRING_TAG) {
961 /* test first because it may be read-only static string */
962 if (!(*(s->value.val.str.rep - 1))) {
963 *(s->value.val.str.rep - 1) = 1;
966 else if (s->value.tag == ARRAY_TAG) {
967 MarkArrayContentsAsUsed((SparseArrayEntry *)s->value.val.arrayPtr);
971 /* Collect all of the strings which remain unreferenced */
972 next = AllocatedStrings;
973 AllocatedStrings = NULL;
974 while (next != NULL) {
975 p = next;
976 next = *((char **)p);
977 if (*(p + sizeof(char *)) != 0) {
978 *((char **)p) = AllocatedStrings;
979 AllocatedStrings = p;
981 else {
982 #ifdef TRACK_GARBAGE_LEAKS
983 --numAllocatedStrings;
984 #endif
985 XtFree(p);
989 nextAP = AllocatedSparseArrayEntries;
990 AllocatedSparseArrayEntries = NULL;
991 while (nextAP != NULL) {
992 thisAP = nextAP;
993 nextAP = (SparseArrayEntryWrapper *)nextAP->next;
994 if (thisAP->inUse != 0) {
995 thisAP->next = (struct SparseArrayEntryWrapper *)AllocatedSparseArrayEntries;
996 AllocatedSparseArrayEntries = thisAP;
998 else {
999 #ifdef TRACK_GARBAGE_LEAKS
1000 --numAllocatedSparseArrayElements;
1001 #endif
1002 XtFree((void *)thisAP);
1006 #ifdef TRACK_GARBAGE_LEAKS
1007 printf("str count = %d\nary count = %d\n", numAllocatedStrings, numAllocatedSparseArrayElements);
1008 #endif
1012 ** Save and restore execution context to data structure "context"
1014 static void saveContext(RestartData *context)
1016 context->stack = TheStack;
1017 context->stackP = StackP;
1018 context->frameP = FrameP;
1019 context->pc = PC;
1020 context->runWindow = InitiatingWindow;
1021 context->focusWindow = FocusWindow;
1024 static void restoreContext(RestartData *context)
1026 TheStack = context->stack;
1027 StackP = context->stackP;
1028 FrameP = context->frameP;
1029 PC = context->pc;
1030 InitiatingWindow = context->runWindow;
1031 FocusWindow = context->focusWindow;
1034 static void freeSymbolTable(Symbol *symTab)
1036 Symbol *s;
1038 while(symTab != NULL) {
1039 s = symTab;
1040 free(s->name);
1041 symTab = s->next;
1042 free((char *)s);
1046 #define POP(dataVal) \
1047 if (StackP == TheStack) \
1048 return execError(StackUnderflowMsg, ""); \
1049 dataVal = *--StackP;
1051 #define PUSH(dataVal) \
1052 if (StackP >= &TheStack[STACK_SIZE]) \
1053 return execError(StackOverflowMsg, ""); \
1054 *StackP++ = dataVal;
1056 #define PEEK(dataVal, peekIndex) \
1057 dataVal = *(StackP - peekIndex - 1);
1059 #define POP_INT(number) \
1060 if (StackP == TheStack) \
1061 return execError(StackUnderflowMsg, ""); \
1062 --StackP; \
1063 if (StackP->tag == STRING_TAG) { \
1064 if (!StringToNum(StackP->val.str.rep, &number)) \
1065 return execError(StringToNumberMsg, ""); \
1066 } else if (StackP->tag == INT_TAG) \
1067 number = StackP->val.n; \
1068 else \
1069 return(execError("can't convert array to integer", NULL));
1071 #define POP_STRING(string) \
1072 if (StackP == TheStack) \
1073 return execError(StackUnderflowMsg, ""); \
1074 --StackP; \
1075 if (StackP->tag == INT_TAG) { \
1076 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1077 sprintf(string, "%d", StackP->val.n); \
1078 } else if (StackP->tag == STRING_TAG) \
1079 string = StackP->val.str.rep; \
1080 else \
1081 return(execError("can't convert array to string", NULL));
1083 #define PEEK_STRING(string, peekIndex) \
1084 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1085 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1086 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \
1088 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1089 string = (StackP - peekIndex - 1)->val.str.rep; \
1091 else { \
1092 return(execError("can't convert array to string", NULL)); \
1095 #define PEEK_INT(number, peekIndex) \
1096 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1097 if (!StringToNum((StackP - peekIndex - 1)->val.str.rep, &number)) { \
1098 return execError(StringToNumberMsg, ""); \
1100 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1101 number = (StackP - peekIndex - 1)->val.n; \
1103 else { \
1104 return(execError("can't convert array to string", NULL)); \
1107 #define PUSH_INT(number) \
1108 if (StackP >= &TheStack[STACK_SIZE]) \
1109 return execError(StackOverflowMsg, ""); \
1110 StackP->tag = INT_TAG; \
1111 StackP->val.n = number; \
1112 StackP++;
1114 #define PUSH_STRING(string, length) \
1115 if (StackP >= &TheStack[STACK_SIZE]) \
1116 return execError(StackOverflowMsg, ""); \
1117 StackP->tag = STRING_TAG; \
1118 StackP->val.str.rep = string; \
1119 StackP->val.str.len = length; \
1120 StackP++;
1122 #define BINARY_NUMERIC_OPERATION(operator) \
1123 int n1, n2; \
1124 DISASM_RT(PC-1, 1); \
1125 STACKDUMP(2, 3); \
1126 POP_INT(n2) \
1127 POP_INT(n1) \
1128 PUSH_INT(n1 operator n2) \
1129 return STAT_OK;
1131 #define UNARY_NUMERIC_OPERATION(operator) \
1132 int n; \
1133 DISASM_RT(PC-1, 1); \
1134 STACKDUMP(1, 3); \
1135 POP_INT(n) \
1136 PUSH_INT(operator n) \
1137 return STAT_OK;
1140 ** copy a symbol's value onto the stack
1141 ** Before: Prog-> [Sym], next, ...
1142 ** TheStack-> next, ...
1143 ** After: Prog-> Sym, [next], ...
1144 ** TheStack-> [symVal], next, ...
1146 static int pushSymVal(void)
1148 Symbol *s;
1149 int nArgs, argNum;
1150 DataValue symVal;
1152 DISASM_RT(PC-1, 2);
1153 STACKDUMP(0, 3);
1155 s = PC->sym;
1156 PC++;
1158 if (s->type == LOCAL_SYM) {
1159 symVal = FP_GET_SYM_VAL(FrameP, s);
1160 } else if (s->type == GLOBAL_SYM || s->type == CONST_SYM) {
1161 symVal = s->value;
1162 } else if (s->type == ARG_SYM) {
1163 nArgs = FP_GET_ARG_COUNT(FrameP);
1164 argNum = s->value.val.n;
1165 if (argNum >= nArgs) {
1166 return execError("referenced undefined argument: %s", s->name);
1168 if (argNum == N_ARGS_ARG_SYM) {
1169 symVal.tag = INT_TAG;
1170 symVal.val.n = nArgs;
1172 else {
1173 symVal = FP_GET_ARG_N(FrameP, argNum);
1175 } else if (s->type == PROC_VALUE_SYM) {
1176 char *errMsg;
1177 if (!(s->value.val.subr)(FocusWindow, NULL, 0,
1178 &symVal, &errMsg)) {
1179 return execError(errMsg, s->name);
1181 } else
1182 return execError("reading non-variable: %s", s->name);
1183 if (symVal.tag == NO_TAG) {
1184 return execError("variable not set: %s", s->name);
1187 PUSH(symVal)
1189 return STAT_OK;
1192 static int pushArgVal(void)
1194 int nArgs, argNum;
1196 DISASM_RT(PC-1, 1);
1197 STACKDUMP(1, 3);
1199 POP_INT(argNum)
1200 --argNum;
1201 nArgs = FP_GET_ARG_COUNT(FrameP);
1202 if (argNum >= nArgs || argNum < 0) {
1203 char argStr[TYPE_INT_STR_SIZE(argNum)];
1204 sprintf(argStr, "%d", argNum + 1);
1205 return execError("referenced undefined argument: $args[%s]", argStr);
1207 PUSH(FP_GET_ARG_N(FrameP, argNum));
1208 return STAT_OK;
1211 static int pushArgCount(void)
1213 DISASM_RT(PC-1, 1);
1214 STACKDUMP(0, 3);
1216 PUSH_INT(FP_GET_ARG_COUNT(FrameP));
1217 return STAT_OK;
1220 static int pushArgArray(void)
1222 int nArgs, argNum;
1223 DataValue argVal, *resultArray;
1225 DISASM_RT(PC-1, 1);
1226 STACKDUMP(0, 3);
1228 nArgs = FP_GET_ARG_COUNT(FrameP);
1229 resultArray = &FP_GET_ARG_ARRAY_CACHE(FrameP);
1230 if (resultArray->tag != ARRAY_TAG) {
1231 resultArray->tag = ARRAY_TAG;
1232 resultArray->val.arrayPtr = ArrayNew();
1234 for (argNum = 0; argNum < nArgs; ++argNum) {
1235 char intStr[TYPE_INT_STR_SIZE(argNum)];
1237 sprintf(intStr, "%d", argNum + 1);
1238 argVal = FP_GET_ARG_N(FrameP, argNum);
1239 if (!ArrayInsert(resultArray, AllocStringCpy(intStr), &argVal)) {
1240 return(execError("array insertion failure", NULL));
1244 PUSH(*resultArray);
1245 return STAT_OK;
1249 ** Push an array (by reference) onto the stack
1250 ** Before: Prog-> [ArraySym], makeEmpty, next, ...
1251 ** TheStack-> next, ...
1252 ** After: Prog-> ArraySym, makeEmpty, [next], ...
1253 ** TheStack-> [elemValue], next, ...
1254 ** makeEmpty is either true (1) or false (0): if true, and the element is not
1255 ** present in the array, create it.
1257 static int pushArraySymVal(void)
1259 Symbol *sym;
1260 DataValue *dataPtr;
1261 int initEmpty;
1263 DISASM_RT(PC-1, 3);
1264 STACKDUMP(0, 3);
1266 sym = PC->sym;
1267 PC++;
1268 initEmpty = PC->value;
1269 PC++;
1271 if (sym->type == LOCAL_SYM) {
1272 dataPtr = &FP_GET_SYM_VAL(FrameP, sym);
1274 else if (sym->type == GLOBAL_SYM) {
1275 dataPtr = &sym->value;
1277 else {
1278 return execError("assigning to non-lvalue array or non-array: %s", sym->name);
1281 if (initEmpty && dataPtr->tag == NO_TAG) {
1282 dataPtr->tag = ARRAY_TAG;
1283 dataPtr->val.arrayPtr = ArrayNew();
1286 if (dataPtr->tag == NO_TAG) {
1287 return execError("variable not set: %s", sym->name);
1290 PUSH(*dataPtr)
1292 return STAT_OK;
1296 ** assign top value to next symbol
1298 ** Before: Prog-> [symbol], next, ...
1299 ** TheStack-> [value], next, ...
1300 ** After: Prog-> symbol, [next], ...
1301 ** TheStack-> next, ...
1303 static int assign(void)
1305 Symbol *sym;
1306 DataValue *dataPtr;
1307 DataValue value;
1309 DISASM_RT(PC-1, 2);
1310 STACKDUMP(1, 3);
1312 sym = PC->sym;
1313 PC++;
1315 if (sym->type != GLOBAL_SYM && sym->type != LOCAL_SYM) {
1316 if (sym->type == ARG_SYM) {
1317 return execError("assignment to function argument: %s", sym->name);
1319 else if (sym->type == PROC_VALUE_SYM) {
1320 return execError("assignment to read-only variable: %s", sym->name);
1322 else {
1323 return execError("assignment to non-variable: %s", sym->name);
1327 if (sym->type == LOCAL_SYM) {
1328 dataPtr = &FP_GET_SYM_VAL(FrameP, sym);
1330 else {
1331 dataPtr = &sym->value;
1334 POP(value)
1336 if (value.tag == ARRAY_TAG) {
1337 ArrayCopy(dataPtr, &value);
1339 else {
1340 *dataPtr = value;
1342 return STAT_OK;
1346 ** copy the top value of the stack
1347 ** Before: TheStack-> value, next, ...
1348 ** After: TheStack-> value, value, next, ...
1350 static int dupStack(void)
1352 DataValue value;
1354 DISASM_RT(PC-1, 1);
1355 STACKDUMP(1, 3);
1357 PEEK(value, 0)
1358 PUSH(value)
1360 return STAT_OK;
1364 ** if left and right arguments are arrays, then the result is a new array
1365 ** in which all the keys from both the right and left are copied
1366 ** the values from the right array are used in the result array when the
1367 ** keys are the same
1368 ** Before: TheStack-> value2, value1, next, ...
1369 ** After: TheStack-> resValue, next, ...
1371 static int add(void)
1373 DataValue leftVal, rightVal, resultArray;
1374 int n1, n2;
1376 DISASM_RT(PC-1, 1);
1377 STACKDUMP(2, 3);
1379 PEEK(rightVal, 0)
1380 if (rightVal.tag == ARRAY_TAG) {
1381 PEEK(leftVal, 1)
1382 if (leftVal.tag == ARRAY_TAG) {
1383 SparseArrayEntry *leftIter, *rightIter;
1384 resultArray.tag = ARRAY_TAG;
1385 resultArray.val.arrayPtr = ArrayNew();
1387 POP(rightVal)
1388 POP(leftVal)
1389 leftIter = arrayIterateFirst(&leftVal);
1390 rightIter = arrayIterateFirst(&rightVal);
1391 while (leftIter || rightIter) {
1392 Boolean insertResult = True;
1394 if (leftIter && rightIter) {
1395 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1396 if (compareResult < 0) {
1397 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1398 leftIter = arrayIterateNext(leftIter);
1400 else if (compareResult > 0) {
1401 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1402 rightIter = arrayIterateNext(rightIter);
1404 else {
1405 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1406 leftIter = arrayIterateNext(leftIter);
1407 rightIter = arrayIterateNext(rightIter);
1410 else if (leftIter) {
1411 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1412 leftIter = arrayIterateNext(leftIter);
1414 else {
1415 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1416 rightIter = arrayIterateNext(rightIter);
1418 if (!insertResult) {
1419 return(execError("array insertion failure", NULL));
1422 PUSH(resultArray)
1424 else {
1425 return(execError("can't mix math with arrays and non-arrays", NULL));
1428 else {
1429 POP_INT(n2)
1430 POP_INT(n1)
1431 PUSH_INT(n1 + n2)
1433 return(STAT_OK);
1437 ** if left and right arguments are arrays, then the result is a new array
1438 ** in which only the keys which exist in the left array but not in the right
1439 ** are copied
1440 ** Before: TheStack-> value2, value1, next, ...
1441 ** After: TheStack-> resValue, next, ...
1443 static int subtract(void)
1445 DataValue leftVal, rightVal, resultArray;
1446 int n1, n2;
1448 DISASM_RT(PC-1, 1);
1449 STACKDUMP(2, 3);
1451 PEEK(rightVal, 0)
1452 if (rightVal.tag == ARRAY_TAG) {
1453 PEEK(leftVal, 1)
1454 if (leftVal.tag == ARRAY_TAG) {
1455 SparseArrayEntry *leftIter, *rightIter;
1456 resultArray.tag = ARRAY_TAG;
1457 resultArray.val.arrayPtr = ArrayNew();
1459 POP(rightVal)
1460 POP(leftVal)
1461 leftIter = arrayIterateFirst(&leftVal);
1462 rightIter = arrayIterateFirst(&rightVal);
1463 while (leftIter) {
1464 Boolean insertResult = True;
1466 if (leftIter && rightIter) {
1467 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1468 if (compareResult < 0) {
1469 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1470 leftIter = arrayIterateNext(leftIter);
1472 else if (compareResult > 0) {
1473 rightIter = arrayIterateNext(rightIter);
1475 else {
1476 leftIter = arrayIterateNext(leftIter);
1477 rightIter = arrayIterateNext(rightIter);
1480 else if (leftIter) {
1481 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1482 leftIter = arrayIterateNext(leftIter);
1484 if (!insertResult) {
1485 return(execError("array insertion failure", NULL));
1488 PUSH(resultArray)
1490 else {
1491 return(execError("can't mix math with arrays and non-arrays", NULL));
1494 else {
1495 POP_INT(n2)
1496 POP_INT(n1)
1497 PUSH_INT(n1 - n2)
1499 return(STAT_OK);
1503 ** Other binary operators
1504 ** Before: TheStack-> value2, value1, next, ...
1505 ** After: TheStack-> resValue, next, ...
1507 ** Other unary operators
1508 ** Before: TheStack-> value, next, ...
1509 ** After: TheStack-> resValue, next, ...
1511 static int multiply(void)
1513 BINARY_NUMERIC_OPERATION(*)
1516 static int divide(void)
1518 int n1, n2;
1520 DISASM_RT(PC-1, 1);
1521 STACKDUMP(2, 3);
1523 POP_INT(n2)
1524 POP_INT(n1)
1525 if (n2 == 0) {
1526 return execError("division by zero", "");
1528 PUSH_INT(n1 / n2)
1529 return STAT_OK;
1532 static int modulo(void)
1534 int n1, n2;
1536 DISASM_RT(PC-1, 1);
1537 STACKDUMP(2, 3);
1539 POP_INT(n2)
1540 POP_INT(n1)
1541 if (n2 == 0) {
1542 return execError("modulo by zero", "");
1544 PUSH_INT(n1 % n2)
1545 return STAT_OK;
1548 static int negate(void)
1550 UNARY_NUMERIC_OPERATION(-)
1553 static int increment(void)
1555 UNARY_NUMERIC_OPERATION(++)
1558 static int decrement(void)
1560 UNARY_NUMERIC_OPERATION(--)
1563 static int gt(void)
1565 BINARY_NUMERIC_OPERATION(>)
1568 static int lt(void)
1570 BINARY_NUMERIC_OPERATION(<)
1573 static int ge(void)
1575 BINARY_NUMERIC_OPERATION(>=)
1578 static int le(void)
1580 BINARY_NUMERIC_OPERATION(<=)
1584 ** verify that compares are between integers and/or strings only
1585 ** Before: TheStack-> value1, value2, next, ...
1586 ** After: TheStack-> resValue, next, ...
1587 ** where resValue is 1 for true, 0 for false
1589 static int eq(void)
1591 DataValue v1, v2;
1593 DISASM_RT(PC-1, 1);
1594 STACKDUMP(2, 3);
1596 POP(v1)
1597 POP(v2)
1598 if (v1.tag == INT_TAG && v2.tag == INT_TAG) {
1599 v1.val.n = v1.val.n == v2.val.n;
1601 else if (v1.tag == STRING_TAG && v2.tag == STRING_TAG) {
1602 v1.val.n = !strcmp(v1.val.str.rep, v2.val.str.rep);
1604 else if (v1.tag == STRING_TAG && v2.tag == INT_TAG) {
1605 int number;
1606 if (!StringToNum(v1.val.str.rep, &number)) {
1607 v1.val.n = 0;
1609 else {
1610 v1.val.n = number == v2.val.n;
1613 else if (v2.tag == STRING_TAG && v1.tag == INT_TAG) {
1614 int number;
1615 if (!StringToNum(v2.val.str.rep, &number)) {
1616 v1.val.n = 0;
1618 else {
1619 v1.val.n = number == v1.val.n;
1622 else {
1623 return(execError("incompatible types to compare", NULL));
1625 v1.tag = INT_TAG;
1626 PUSH(v1)
1627 return(STAT_OK);
1630 /* negated eq() call */
1631 static int ne(void)
1633 eq();
1634 return not();
1638 ** if left and right arguments are arrays, then the result is a new array
1639 ** in which only the keys which exist in both the right or left are copied
1640 ** the values from the right array are used in the result array
1641 ** Before: TheStack-> value2, value1, next, ...
1642 ** After: TheStack-> resValue, next, ...
1644 static int bitAnd(void)
1646 DataValue leftVal, rightVal, resultArray;
1647 int n1, n2;
1649 DISASM_RT(PC-1, 1);
1650 STACKDUMP(2, 3);
1652 PEEK(rightVal, 0)
1653 if (rightVal.tag == ARRAY_TAG) {
1654 PEEK(leftVal, 1)
1655 if (leftVal.tag == ARRAY_TAG) {
1656 SparseArrayEntry *leftIter, *rightIter;
1657 resultArray.tag = ARRAY_TAG;
1658 resultArray.val.arrayPtr = ArrayNew();
1660 POP(rightVal)
1661 POP(leftVal)
1662 leftIter = arrayIterateFirst(&leftVal);
1663 rightIter = arrayIterateFirst(&rightVal);
1664 while (leftIter && rightIter) {
1665 Boolean insertResult = True;
1666 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1668 if (compareResult < 0) {
1669 leftIter = arrayIterateNext(leftIter);
1671 else if (compareResult > 0) {
1672 rightIter = arrayIterateNext(rightIter);
1674 else {
1675 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1676 leftIter = arrayIterateNext(leftIter);
1677 rightIter = arrayIterateNext(rightIter);
1679 if (!insertResult) {
1680 return(execError("array insertion failure", NULL));
1683 PUSH(resultArray)
1685 else {
1686 return(execError("can't mix math with arrays and non-arrays", NULL));
1689 else {
1690 POP_INT(n2)
1691 POP_INT(n1)
1692 PUSH_INT(n1 & n2)
1694 return(STAT_OK);
1698 ** if left and right arguments are arrays, then the result is a new array
1699 ** in which only the keys which exist in either the right or left but not both
1700 ** are copied
1701 ** Before: TheStack-> value2, value1, next, ...
1702 ** After: TheStack-> resValue, next, ...
1704 static int bitOr(void)
1706 DataValue leftVal, rightVal, resultArray;
1707 int n1, n2;
1709 DISASM_RT(PC-1, 1);
1710 STACKDUMP(2, 3);
1712 PEEK(rightVal, 0)
1713 if (rightVal.tag == ARRAY_TAG) {
1714 PEEK(leftVal, 1)
1715 if (leftVal.tag == ARRAY_TAG) {
1716 SparseArrayEntry *leftIter, *rightIter;
1717 resultArray.tag = ARRAY_TAG;
1718 resultArray.val.arrayPtr = ArrayNew();
1720 POP(rightVal)
1721 POP(leftVal)
1722 leftIter = arrayIterateFirst(&leftVal);
1723 rightIter = arrayIterateFirst(&rightVal);
1724 while (leftIter || rightIter) {
1725 Boolean insertResult = True;
1727 if (leftIter && rightIter) {
1728 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
1729 if (compareResult < 0) {
1730 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1731 leftIter = arrayIterateNext(leftIter);
1733 else if (compareResult > 0) {
1734 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1735 rightIter = arrayIterateNext(rightIter);
1737 else {
1738 leftIter = arrayIterateNext(leftIter);
1739 rightIter = arrayIterateNext(rightIter);
1742 else if (leftIter) {
1743 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
1744 leftIter = arrayIterateNext(leftIter);
1746 else {
1747 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
1748 rightIter = arrayIterateNext(rightIter);
1750 if (!insertResult) {
1751 return(execError("array insertion failure", NULL));
1754 PUSH(resultArray)
1756 else {
1757 return(execError("can't mix math with arrays and non-arrays", NULL));
1760 else {
1761 POP_INT(n2)
1762 POP_INT(n1)
1763 PUSH_INT(n1 | n2)
1765 return(STAT_OK);
1768 static int and(void)
1770 BINARY_NUMERIC_OPERATION(&&)
1773 static int or(void)
1775 BINARY_NUMERIC_OPERATION(||)
1778 static int not(void)
1780 UNARY_NUMERIC_OPERATION(!)
1784 ** raise one number to the power of another
1785 ** Before: TheStack-> raisedBy, number, next, ...
1786 ** After: TheStack-> result, next, ...
1788 static int power(void)
1790 int n1, n2, n3;
1792 DISASM_RT(PC-1, 1);
1793 STACKDUMP(2, 3);
1795 POP_INT(n2)
1796 POP_INT(n1)
1797 /* We need to round to deal with pow() giving results slightly above
1798 or below the real result since it deals with floating point numbers.
1799 Note: We're not really wanting rounded results, we merely
1800 want to deal with this simple issue. So, 2^-2 = .5, but we
1801 don't want to round this to 1. This is mainly intended to deal with
1802 4^2 = 15.999996 and 16.000001.
1804 if (n2 < 0 && n1 != 1 && n1 != -1) {
1805 if (n1 != 0) {
1806 /* since we're integer only, nearly all negative exponents result in 0 */
1807 n3 = 0;
1809 else {
1810 /* allow error to occur */
1811 n3 = (int)pow((double)n1, (double)n2);
1814 else {
1815 if ((n1 < 0) && (n2 & 1)) {
1816 /* round to nearest integer for negative values*/
1817 n3 = (int)(pow((double)n1, (double)n2) - (double)0.5);
1819 else {
1820 /* round to nearest integer for positive values*/
1821 n3 = (int)(pow((double)n1, (double)n2) + (double)0.5);
1824 PUSH_INT(n3)
1825 return errCheck("exponentiation");
1829 ** concatenate two top items on the stack
1830 ** Before: TheStack-> str2, str1, next, ...
1831 ** After: TheStack-> result, next, ...
1833 static int concat(void)
1835 char *s1, *s2, *out;
1836 int len1, len2;
1838 DISASM_RT(PC-1, 1);
1839 STACKDUMP(2, 3);
1841 POP_STRING(s2)
1842 POP_STRING(s1)
1843 len1 = strlen(s1);
1844 len2 = strlen(s2);
1845 out = AllocString(len1 + len2 + 1);
1846 strncpy(out, s1, len1);
1847 strcpy(&out[len1], s2);
1848 PUSH_STRING(out, len1 + len2)
1849 return STAT_OK;
1853 ** Call a subroutine or function (user defined or built-in). Args are the
1854 ** subroutine's symbol, and the number of arguments which have been pushed
1855 ** on the stack.
1857 ** For a macro subroutine, the return address, frame pointer, number of
1858 ** arguments and space for local variables are added to the stack, and the
1859 ** PC is set to point to the new function. For a built-in routine, the
1860 ** arguments are popped off the stack, and the routine is just called.
1862 ** Before: Prog-> [subrSym], nArgs, next, ...
1863 ** TheStack-> argN-arg1, next, ...
1864 ** After: Prog-> next, ... -- (built-in called subr)
1865 ** TheStack-> retVal?, next, ...
1866 ** or: Prog-> (in called)next, ... -- (macro code called subr)
1867 ** TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
1869 static int callSubroutine(void)
1871 Symbol *sym, *s;
1872 int i, nArgs;
1873 static DataValue noValue = {NO_TAG, {0}};
1874 Program *prog;
1875 char *errMsg;
1877 sym = PC->sym;
1878 PC++;
1879 nArgs = PC->value;
1880 PC++;
1882 DISASM_RT(PC-3, 3);
1883 STACKDUMP(nArgs, 3);
1886 ** If the subroutine is built-in, call the built-in routine
1888 if (sym->type == C_FUNCTION_SYM) {
1889 DataValue result;
1891 /* "pop" stack back to the first argument in the call stack */
1892 StackP -= nArgs;
1894 /* Call the function and check for preemption */
1895 PreemptRequest = False;
1896 if (!sym->value.val.subr(FocusWindow, StackP,
1897 nArgs, &result, &errMsg))
1898 return execError(errMsg, sym->name);
1899 if (PC->func == fetchRetVal) {
1900 if (result.tag == NO_TAG) {
1901 return execError("%s does not return a value", sym->name);
1903 PUSH(result);
1904 PC++;
1906 return PreemptRequest ? STAT_PREEMPT : STAT_OK;
1910 ** Call a macro subroutine:
1912 ** Push all of the required information to resume, and make space on the
1913 ** stack for local variables (and initialize them), on top of the argument
1914 ** values which are already there.
1916 if (sym->type == MACRO_FUNCTION_SYM) {
1917 StackP->tag = NO_TAG; /* return PC */
1918 StackP->val.inst = PC;
1919 StackP++;
1921 StackP->tag = NO_TAG; /* old FrameP */
1922 StackP->val.dataval = FrameP;
1923 StackP++;
1925 StackP->tag = NO_TAG; /* nArgs */
1926 StackP->val.n = nArgs;
1927 StackP++;
1929 *(StackP++) = noValue; /* cached arg array */
1931 FrameP = StackP;
1932 prog = (Program *)sym->value.val.str.rep;
1933 PC = prog->code;
1934 for (s = prog->localSymList; s != NULL; s = s->next) {
1935 FP_GET_SYM_VAL(FrameP, s) = noValue;
1936 StackP++;
1938 return STAT_OK;
1942 ** Call an action routine
1944 if (sym->type == ACTION_ROUTINE_SYM) {
1945 String argList[MAX_ARGS];
1946 Cardinal numArgs = nArgs;
1947 XKeyEvent key_event;
1948 Display *disp;
1949 Window win;
1951 /* Create a fake event with a timestamp suitable for actions which need
1952 timestamps, a marker to indicate that the call was from a macro
1953 (to stop shell commands from putting up their own separate banner) */
1954 disp=XtDisplay(InitiatingWindow->shell);
1955 win=XtWindow(InitiatingWindow->shell);
1957 key_event.type = KeyPress;
1958 key_event.send_event = MACRO_EVENT_MARKER;
1959 key_event.time=XtLastTimestampProcessed(XtDisplay(InitiatingWindow->shell));
1961 /* The following entries are just filled in to avoid problems
1962 in strange cases, like calling "self_insert()" directly from the
1963 macro menu. In fact the display was sufficient to cure this crash. */
1964 key_event.display=disp;
1965 key_event.window=key_event.root=key_event.subwindow=win;
1967 /* pop arguments off the stack and put them in the argument list */
1968 for (i=nArgs-1; i>=0; i--) {
1969 POP_STRING(argList[i])
1972 /* Call the action routine and check for preemption */
1973 PreemptRequest = False;
1974 sym->value.val.xtproc(FocusWindow->lastFocus,
1975 (XEvent *)&key_event, argList, &numArgs);
1976 if (PC->func == fetchRetVal) {
1977 return execError("%s does not return a value", sym->name);
1979 return PreemptRequest ? STAT_PREEMPT : STAT_OK;
1982 /* Calling a non subroutine symbol */
1983 return execError("%s is not a function or subroutine", sym->name);
1987 ** This should never be executed, returnVal checks for the presence of this
1988 ** instruction at the PC to decide whether to push the function's return
1989 ** value, then skips over it without executing.
1991 static int fetchRetVal(void)
1993 return execError("internal error: frv", NULL);
1996 /* see comments for returnValOrNone() */
1997 static int returnNoVal(void)
1999 return returnValOrNone(False);
2001 static int returnVal(void)
2003 return returnValOrNone(True);
2007 ** Return from a subroutine call
2008 ** Before: Prog-> [next], ...
2009 ** TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
2010 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
2011 ** TheStack-> retVal?, next, ...
2013 static int returnValOrNone(int valOnStack)
2015 DataValue retVal;
2016 static DataValue noValue = {NO_TAG, {0}};
2017 DataValue *newFrameP;
2018 int nArgs;
2020 DISASM_RT(PC-1, 1);
2021 STACKDUMP(StackP - FrameP + FP_GET_ARG_COUNT(FrameP) + FP_TO_ARGS_DIST, 3);
2023 /* return value is on the stack */
2024 if (valOnStack) {
2025 POP(retVal);
2028 /* get stored return information */
2029 nArgs = FP_GET_ARG_COUNT(FrameP);
2030 newFrameP = FP_GET_OLD_FP(FrameP);
2031 PC = FP_GET_RET_PC(FrameP);
2033 /* pop past local variables */
2034 StackP = FrameP;
2035 /* pop past function arguments */
2036 StackP -= (FP_TO_ARGS_DIST + nArgs);
2037 FrameP = newFrameP;
2039 /* push returned value, if requsted */
2040 if (PC == NULL) {
2041 if (valOnStack) {
2042 PUSH(retVal);
2043 } else {
2044 PUSH(noValue);
2046 } else if (PC->func == fetchRetVal) {
2047 if (valOnStack) {
2048 PUSH(retVal);
2049 PC++;
2050 } else {
2051 return execError(
2052 "using return value of %s which does not return a value",
2053 ((PC-2)->sym->name));
2057 /* NULL return PC indicates end of program */
2058 return PC == NULL ? STAT_DONE : STAT_OK;
2062 ** Unconditional branch offset by immediate operand
2064 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2065 ** After: Prog-> branchDest, next, ..., (branchdest)[next]
2067 static int branch(void)
2069 DISASM_RT(PC-1, 2);
2070 STACKDUMP(0, 3);
2072 PC += PC->value;
2073 return STAT_OK;
2077 ** Conditional branches if stack value is True/False (non-zero/0) to address
2078 ** of immediate operand (pops stack)
2080 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2081 ** After: either: Prog-> branchDest, [next], ...
2082 ** After: or: Prog-> branchDest, next, ..., (branchdest)[next]
2084 static int branchTrue(void)
2086 int value;
2087 Inst *addr;
2089 DISASM_RT(PC-1, 2);
2090 STACKDUMP(1, 3);
2092 POP_INT(value)
2093 addr = PC + PC->value;
2094 PC++;
2096 if (value)
2097 PC = addr;
2098 return STAT_OK;
2100 static int branchFalse(void)
2102 int value;
2103 Inst *addr;
2105 DISASM_RT(PC-1, 2);
2106 STACKDUMP(1, 3);
2108 POP_INT(value)
2109 addr = PC + PC->value;
2110 PC++;
2112 if (!value)
2113 PC = addr;
2114 return STAT_OK;
2118 ** Ignore the address following the instruction and continue. Why? So
2119 ** some code that uses conditional branching doesn't have to figure out
2120 ** whether to store a branch address.
2122 ** Before: Prog-> [branchDest], next, ...
2123 ** After: Prog-> branchDest, [next], ...
2125 static int branchNever(void)
2127 DISASM_RT(PC-1, 2);
2128 STACKDUMP(0, 3);
2130 PC++;
2131 return STAT_OK;
2135 ** recursively copy(duplicate) the sparse array nodes of an array
2136 ** this does not duplicate the key/node data since they are never
2137 ** modified, only replaced
2139 int ArrayCopy(DataValue *dstArray, DataValue *srcArray)
2141 SparseArrayEntry *srcIter;
2143 dstArray->tag = ARRAY_TAG;
2144 dstArray->val.arrayPtr = ArrayNew();
2146 srcIter = arrayIterateFirst(srcArray);
2147 while (srcIter) {
2148 if (srcIter->value.tag == ARRAY_TAG) {
2149 int errNum;
2150 DataValue tmpArray;
2152 errNum = ArrayCopy(&tmpArray, &srcIter->value);
2153 if (errNum != STAT_OK) {
2154 return(errNum);
2156 if (!ArrayInsert(dstArray, srcIter->key, &tmpArray)) {
2157 return(execError("array copy failed", NULL));
2160 else {
2161 if (!ArrayInsert(dstArray, srcIter->key, &srcIter->value)) {
2162 return(execError("array copy failed", NULL));
2165 srcIter = arrayIterateNext(srcIter);
2167 return(STAT_OK);
2171 ** creates an allocated string of a single key for all the sub-scripts
2172 ** using ARRAY_DIM_SEP as a separator
2173 ** this function uses the PEEK macros in order to remove most limits on
2174 ** the number of arguments to an array
2175 ** I really need to optimize the size approximation rather than assuming
2176 ** a worst case size for every integer argument
2178 static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams)
2180 DataValue tmpVal;
2181 int sepLen = strlen(ARRAY_DIM_SEP);
2182 int keyLength = 0;
2183 int i;
2185 keyLength = sepLen * (nArgs - 1);
2186 for (i = nArgs - 1; i >= 0; --i) {
2187 PEEK(tmpVal, i)
2188 if (tmpVal.tag == INT_TAG) {
2189 keyLength += TYPE_INT_STR_SIZE(tmpVal.val.n);
2191 else if (tmpVal.tag == STRING_TAG) {
2192 keyLength += tmpVal.val.str.len;
2194 else {
2195 return(execError("can only index array with string or int.", NULL));
2198 *keyString = AllocString(keyLength + 1);
2199 (*keyString)[0] = 0;
2200 for (i = nArgs - 1; i >= 0; --i) {
2201 if (i != nArgs - 1) {
2202 strcat(*keyString, ARRAY_DIM_SEP);
2204 PEEK(tmpVal, i)
2205 if (tmpVal.tag == INT_TAG) {
2206 sprintf(&((*keyString)[strlen(*keyString)]), "%d", tmpVal.val.n);
2208 else if (tmpVal.tag == STRING_TAG) {
2209 strcat(*keyString, tmpVal.val.str.rep);
2211 else {
2212 return(execError("can only index array with string or int.", NULL));
2215 if (!leaveParams) {
2216 for (i = nArgs - 1; i >= 0; --i) {
2217 POP(tmpVal)
2220 return(STAT_OK);
2224 ** allocate an empty array node, this is used as the root node and never
2225 ** contains any data, only refernces to other nodes
2227 static rbTreeNode *arrayEmptyAllocator(void)
2229 SparseArrayEntry *newNode = allocateSparseArrayEntry();
2230 if (newNode) {
2231 newNode->key = NULL;
2232 newNode->value.tag = NO_TAG;
2234 return((rbTreeNode *)newNode);
2238 ** create and copy array node and copy contents, we merely copy pointers
2239 ** since they are never modified, only replaced
2241 static rbTreeNode *arrayAllocateNode(rbTreeNode *src)
2243 SparseArrayEntry *newNode = allocateSparseArrayEntry();
2244 if (newNode) {
2245 newNode->key = ((SparseArrayEntry *)src)->key;
2246 newNode->value = ((SparseArrayEntry *)src)->value;
2248 return((rbTreeNode *)newNode);
2252 ** copy array node data, we merely copy pointers since they are never
2253 ** modified, only replaced
2255 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src)
2257 ((SparseArrayEntry *)dst)->key = ((SparseArrayEntry *)src)->key;
2258 ((SparseArrayEntry *)dst)->value = ((SparseArrayEntry *)src)->value;
2259 return(1);
2263 ** compare two array nodes returning an integer value similar to strcmp()
2265 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right)
2267 return(strcmp(((SparseArrayEntry *)left)->key, ((SparseArrayEntry *)right)->key));
2271 ** dispose an array node, garbage collection handles this, so we mark it
2272 ** to allow iterators in macro language to determine they have been unlinked
2274 static void arrayDisposeNode(rbTreeNode *src)
2276 /* Let garbage collection handle this but mark it so iterators can tell */
2277 src->left = NULL;
2278 src->right = NULL;
2279 src->parent = NULL;
2280 src->color = -1;
2283 struct SparseArrayEntry *ArrayNew(void)
2285 return((struct SparseArrayEntry *)rbTreeNew(arrayEmptyAllocator));
2289 ** insert a DataValue into an array, allocate the array if needed
2290 ** keyStr must be a string that was allocated with AllocString()
2292 Boolean ArrayInsert(DataValue* theArray, char* keyStr, DataValue* theValue)
2294 SparseArrayEntry tmpEntry;
2295 rbTreeNode *insertedNode;
2297 tmpEntry.key = keyStr;
2298 tmpEntry.value = *theValue;
2300 if (theArray->val.arrayPtr == NULL) {
2301 theArray->val.arrayPtr = ArrayNew();
2304 if (theArray->val.arrayPtr != NULL) {
2305 insertedNode = rbTreeInsert((rbTreeNode*) (theArray->val.arrayPtr),
2306 (rbTreeNode *)&tmpEntry, arrayEntryCompare, arrayAllocateNode,
2307 arrayEntryCopyToNode);
2309 if (insertedNode) {
2310 return True;
2311 } else {
2312 return False;
2316 return False;
2320 ** remove a node from an array whose key matches keyStr
2322 void ArrayDelete(DataValue *theArray, char *keyStr)
2324 SparseArrayEntry searchEntry;
2326 if (theArray->val.arrayPtr) {
2327 searchEntry.key = keyStr;
2328 rbTreeDelete((rbTreeNode *)theArray->val.arrayPtr, (rbTreeNode *)&searchEntry,
2329 arrayEntryCompare, arrayDisposeNode);
2334 ** remove all nodes from an array
2336 void ArrayDeleteAll(DataValue *theArray)
2338 if (theArray->val.arrayPtr) {
2339 rbTreeNode *iter = rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
2340 while (iter) {
2341 rbTreeNode *nextIter = rbTreeNext(iter);
2342 rbTreeDeleteNode((rbTreeNode *)theArray->val.arrayPtr,
2343 iter, arrayDisposeNode);
2345 iter = nextIter;
2351 ** returns the number of elements (nodes containing values) of an array
2353 unsigned ArraySize(DataValue* theArray)
2355 if (theArray->val.arrayPtr) {
2356 return rbTreeSize((rbTreeNode *)theArray->val.arrayPtr);
2357 } else {
2358 return 0;
2363 ** retrieves an array node whose key matches
2364 ** returns 1 for success 0 for not found
2366 Boolean ArrayGet(DataValue* theArray, char* keyStr, DataValue* theValue)
2368 SparseArrayEntry searchEntry;
2369 rbTreeNode *foundNode;
2371 if (theArray->val.arrayPtr) {
2372 searchEntry.key = keyStr;
2373 foundNode = rbTreeFind((rbTreeNode*) theArray->val.arrayPtr,
2374 (rbTreeNode*) &searchEntry, arrayEntryCompare);
2375 if (foundNode) {
2376 *theValue = ((SparseArrayEntry*) foundNode)->value;
2377 return True;
2381 return False;
2385 ** get pointer to start iterating an array
2387 SparseArrayEntry *arrayIterateFirst(DataValue *theArray)
2389 SparseArrayEntry *startPos;
2390 if (theArray->val.arrayPtr) {
2391 startPos = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
2393 else {
2394 startPos = NULL;
2396 return(startPos);
2400 ** move iterator to next entry in array
2402 SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator)
2404 SparseArrayEntry *nextPos;
2405 if (iterator) {
2406 nextPos = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)iterator);
2408 else {
2409 nextPos = NULL;
2411 return(nextPos);
2415 ** evaluate an array element and push the result onto the stack
2417 ** Before: Prog-> [nDim], next, ...
2418 ** TheStack-> indnDim, ... ind1, ArraySym, next, ...
2419 ** After: Prog-> nDim, [next], ...
2420 ** TheStack-> indexedArrayVal, next, ...
2422 static int arrayRef(void)
2424 int errNum;
2425 DataValue srcArray, valueItem;
2426 char *keyString = NULL;
2427 int nDim;
2429 nDim = PC->value;
2430 PC++;
2432 DISASM_RT(PC-2, 2);
2433 STACKDUMP(nDim, 3);
2435 if (nDim > 0) {
2436 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2437 if (errNum != STAT_OK) {
2438 return(errNum);
2441 POP(srcArray)
2442 if (srcArray.tag == ARRAY_TAG) {
2443 if (!ArrayGet(&srcArray, keyString, &valueItem)) {
2444 return(execError("referenced array value not in array: %s", keyString));
2446 PUSH(valueItem)
2447 return(STAT_OK);
2449 else {
2450 return(execError("operator [] on non-array", NULL));
2453 else {
2454 POP(srcArray)
2455 if (srcArray.tag == ARRAY_TAG) {
2456 PUSH_INT(ArraySize(&srcArray))
2457 return(STAT_OK);
2459 else {
2460 return(execError("operator [] on non-array", NULL));
2466 ** assign to an array element of a referenced array on the stack
2468 ** Before: Prog-> [nDim], next, ...
2469 ** TheStack-> rhs, indnDim, ... ind1, ArraySym, next, ...
2470 ** After: Prog-> nDim, [next], ...
2471 ** TheStack-> next, ...
2473 static int arrayAssign(void)
2475 char *keyString = NULL;
2476 DataValue srcValue, dstArray;
2477 int errNum;
2478 int nDim;
2480 nDim = PC->value;
2481 PC++;
2483 DISASM_RT(PC-2, 1);
2484 STACKDUMP(nDim, 3);
2486 if (nDim > 0) {
2487 POP(srcValue)
2489 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2490 if (errNum != STAT_OK) {
2491 return(errNum);
2494 POP(dstArray)
2496 if (dstArray.tag != ARRAY_TAG && dstArray.tag != NO_TAG) {
2497 return(execError("cannot assign array element of non-array", NULL));
2499 if (srcValue.tag == ARRAY_TAG) {
2500 DataValue arrayCopyValue;
2502 errNum = ArrayCopy(&arrayCopyValue, &srcValue);
2503 srcValue = arrayCopyValue;
2504 if (errNum != STAT_OK) {
2505 return(errNum);
2508 if (ArrayInsert(&dstArray, keyString, &srcValue)) {
2509 return(STAT_OK);
2511 else {
2512 return(execError("array member allocation failure", NULL));
2515 return(execError("empty operator []", NULL));
2519 ** for use with assign-op operators (eg a[i,j] += k
2521 ** Before: Prog-> [binOp], nDim, next, ...
2522 ** TheStack-> [rhs], indnDim, ... ind1, next, ...
2523 ** After: Prog-> binOp, nDim, [next], ...
2524 ** TheStack-> [rhs], arrayValue, next, ...
2526 static int arrayRefAndAssignSetup(void)
2528 int errNum;
2529 DataValue srcArray, valueItem, moveExpr;
2530 char *keyString = NULL;
2531 int binaryOp, nDim;
2533 binaryOp = PC->value;
2534 PC++;
2535 nDim = PC->value;
2536 PC++;
2538 DISASM_RT(PC-3, 3);
2539 STACKDUMP(nDim + 1, 3);
2541 if (binaryOp) {
2542 POP(moveExpr)
2545 if (nDim > 0) {
2546 errNum = makeArrayKeyFromArgs(nDim, &keyString, 1);
2547 if (errNum != STAT_OK) {
2548 return(errNum);
2551 PEEK(srcArray, nDim)
2552 if (srcArray.tag == ARRAY_TAG) {
2553 if (!ArrayGet(&srcArray, keyString, &valueItem)) {
2554 return(execError("referenced array value not in array: %s", keyString));
2556 PUSH(valueItem)
2557 if (binaryOp) {
2558 PUSH(moveExpr)
2560 return(STAT_OK);
2562 else {
2563 return(execError("operator [] on non-array", NULL));
2566 else {
2567 return(execError("array[] not an lvalue", NULL));
2572 ** setup symbol values for array iteration in interpreter
2574 ** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ...
2575 ** TheStack-> [arrayVal], next, ...
2576 ** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ...
2577 ** TheStack-> [next], ...
2578 ** Where:
2579 ** iter is a symbol which gives the position of the iterator value in
2580 ** the stack frame
2581 ** arrayVal is the data value holding the array in question
2583 static int beginArrayIter(void)
2585 Symbol *iterator;
2586 DataValue *iteratorValPtr;
2587 DataValue arrayVal;
2589 DISASM_RT(PC-1, 2);
2590 STACKDUMP(1, 3);
2592 iterator = PC->sym;
2593 PC++;
2595 POP(arrayVal)
2597 if (iterator->type == LOCAL_SYM) {
2598 iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
2600 else {
2601 return(execError("bad temporary iterator: %s", iterator->name));
2604 iteratorValPtr->tag = INT_TAG;
2605 if (arrayVal.tag != ARRAY_TAG) {
2606 return(execError("can't iterate non-array", NULL));
2609 iteratorValPtr->val.arrayPtr = (struct SparseArrayEntry *)arrayIterateFirst(&arrayVal);
2610 return(STAT_OK);
2614 ** copy key to symbol if node is still valid, marked bad by a color of -1
2615 ** then move iterator to next node
2616 ** this allows iterators to progress even if you delete any node in the array
2617 ** except the item just after the current key
2619 ** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ...
2620 ** TheStack-> [next], ...
2621 ** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ...
2622 ** TheStack-> [next], ... (unchanged)
2623 ** Where:
2624 ** iter is a symbol which gives the position of the iterator value in
2625 ** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers
2626 ** to the array and a position within it
2627 ** iterVal is the programmer-visible symbol which will take the current
2628 ** key value
2629 ** endLoopBranch is the instruction offset to the instruction following the
2630 ** loop (measured from itself)
2631 ** arrayVal is the data value holding the array in question
2632 ** The return-to-start-of-loop branch (at the end of the loop) should address
2633 ** the ARRAY_ITER instruction
2635 static int arrayIter(void)
2637 Symbol *iterator;
2638 Symbol *item;
2639 DataValue *iteratorValPtr;
2640 DataValue *itemValPtr;
2641 SparseArrayEntry *thisEntry;
2642 Inst *branchAddr;
2644 DISASM_RT(PC-1, 4);
2645 STACKDUMP(0, 3);
2647 item = PC->sym;
2648 PC++;
2649 iterator = PC->sym;
2650 PC++;
2651 branchAddr = PC + PC->value;
2652 PC++;
2654 if (item->type == LOCAL_SYM) {
2655 itemValPtr = &FP_GET_SYM_VAL(FrameP, item);
2657 else if (item->type == GLOBAL_SYM) {
2658 itemValPtr = &(item->value);
2660 else {
2661 return(execError("can't assign to: %s", item->name));
2663 itemValPtr->tag = NO_TAG;
2665 if (iterator->type == LOCAL_SYM) {
2666 iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
2668 else {
2669 return(execError("bad temporary iterator: %s", iterator->name));
2672 thisEntry = (SparseArrayEntry *)iteratorValPtr->val.arrayPtr;
2673 if (thisEntry && thisEntry->nodePtrs.color != -1) {
2674 itemValPtr->tag = STRING_TAG;
2675 itemValPtr->val.str.rep = thisEntry->key;
2676 itemValPtr->val.str.len = strlen(thisEntry->key);
2678 iteratorValPtr->val.arrayPtr = (struct SparseArrayEntry *)arrayIterateNext(thisEntry);
2680 else {
2681 PC = branchAddr;
2683 return(STAT_OK);
2687 ** determine if a key or keys exists in an array
2688 ** if the left argument is a string or integer a single check is performed
2689 ** if the key exists, 1 is pushed onto the stack, otherwise 0
2690 ** if the left argument is an array 1 is pushed onto the stack if every key
2691 ** in the left array exists in the right array, otherwise 0
2693 ** Before: Prog-> [next], ...
2694 ** TheStack-> [ArraySym], inSymbol, next, ...
2695 ** After: Prog-> [next], ... -- (unchanged)
2696 ** TheStack-> next, ...
2698 static int inArray(void)
2700 DataValue theArray, leftArray, theValue;
2701 char *keyStr;
2702 int inResult = 0;
2704 DISASM_RT(PC-1, 1);
2705 STACKDUMP(2, 3);
2707 POP(theArray)
2708 if (theArray.tag != ARRAY_TAG) {
2709 return(execError("operator in on non-array", NULL));
2711 PEEK(leftArray, 0)
2712 if (leftArray.tag == ARRAY_TAG) {
2713 SparseArrayEntry *iter;
2715 POP(leftArray)
2716 inResult = 1;
2717 iter = arrayIterateFirst(&leftArray);
2718 while (inResult && iter) {
2719 inResult = inResult && ArrayGet(&theArray, iter->key, &theValue);
2720 iter = arrayIterateNext(iter);
2723 else {
2724 POP_STRING(keyStr)
2725 if (ArrayGet(&theArray, keyStr, &theValue)) {
2726 inResult = 1;
2729 PUSH_INT(inResult)
2730 return(STAT_OK);
2734 ** remove a given key from an array unless nDim is 0, then all keys are removed
2736 ** for use with assign-op operators (eg a[i,j] += k
2737 ** Before: Prog-> [nDim], next, ...
2738 ** TheStack-> [indnDim], ... ind1, arrayValue, next, ...
2739 ** After: Prog-> nDim, [next], ...
2740 ** TheStack-> next, ...
2742 static int deleteArrayElement(void)
2744 DataValue theArray;
2745 char *keyString = NULL;
2746 int nDim;
2748 nDim = PC->value;
2749 PC++;
2751 DISASM_RT(PC-2, 2);
2752 STACKDUMP(nDim + 1, 3);
2754 if (nDim > 0) {
2755 int errNum;
2757 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
2758 if (errNum != STAT_OK) {
2759 return(errNum);
2763 POP(theArray)
2764 if (theArray.tag == ARRAY_TAG) {
2765 if (nDim > 0) {
2766 ArrayDelete(&theArray, keyString);
2768 else {
2769 ArrayDeleteAll(&theArray);
2772 else {
2773 return(execError("attempt to delete from non-array", NULL));
2775 return(STAT_OK);
2779 ** checks errno after operations which can set it. If an error occured,
2780 ** creates appropriate error messages and returns false
2782 static int errCheck(const char *s)
2784 if (errno == EDOM)
2785 return execError("%s argument out of domain", s);
2786 else if (errno == ERANGE)
2787 return execError("%s result out of range", s);
2788 else
2789 return STAT_OK;
2793 ** combine two strings in a static area and set ErrMsg to point to the
2794 ** result. Returns false so a single return execError() statement can
2795 ** be used to both process the message and return.
2797 static int execError(const char *s1, const char *s2)
2799 static char msg[MAX_ERR_MSG_LEN];
2801 sprintf(msg, s1, s2);
2802 ErrMsg = msg;
2803 return STAT_ERROR;
2806 int StringToNum(const char *string, int *number)
2808 const char *c = string;
2810 while (*c == ' ' || *c == '\t') {
2811 ++c;
2813 if (*c == '+' || *c == '-') {
2814 ++c;
2816 while (isdigit((unsigned char)*c)) {
2817 ++c;
2819 while (*c == ' ' || *c == '\t') {
2820 ++c;
2822 if (*c) {
2823 /* if everything went as expected, we should be at end, but we're not */
2824 return False;
2826 if (number) {
2827 if (sscanf(string, "%d", number) != 1) {
2828 /* This case is here to support old behavior */
2829 *number = 0;
2832 return True;
2835 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2836 static void dumpVal(DataValue dv)
2838 switch (dv.tag) {
2839 case INT_TAG:
2840 printf("i=%d", dv.val.n);
2841 break;
2842 case STRING_TAG:
2844 int k;
2845 char s[21];
2846 char *src = dv.val.str.rep;
2847 if (!src) {
2848 printf("s=<NULL>");
2850 else {
2851 for (k = 0; src[k] && k < sizeof s - 1; k++) {
2852 s[k] = isprint(src[k]) ? src[k] : '?';
2854 s[k] = 0;
2855 printf("s=\"%s\"%s[%d]", s,
2856 src[k] ? "..." : "", strlen(src));
2859 break;
2860 case ARRAY_TAG:
2861 printf("<array>");
2862 break;
2863 case NO_TAG:
2864 if (!dv.val.inst) {
2865 printf("<no value>");
2867 else {
2868 printf("?%8p", dv.val.inst);
2870 break;
2871 default:
2872 printf("UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst);
2873 break;
2876 #endif /* #ifdef DEBUG_DISASSEMBLER */
2878 #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */
2879 static void disasm(Inst *inst, int nInstr)
2881 static const char *opNames[N_OPS] = {
2882 "RETURN_NO_VAL", /* returnNoVal */
2883 "RETURN", /* returnVal */
2884 "PUSH_SYM", /* pushSymVal */
2885 "DUP", /* dupStack */
2886 "ADD", /* add */
2887 "SUB", /* subtract */
2888 "MUL", /* multiply */
2889 "DIV", /* divide */
2890 "MOD", /* modulo */
2891 "NEGATE", /* negate */
2892 "INCR", /* increment */
2893 "DECR", /* decrement */
2894 "GT", /* gt */
2895 "LT", /* lt */
2896 "GE", /* ge */
2897 "LE", /* le */
2898 "EQ", /* eq */
2899 "NE", /* ne */
2900 "BIT_AND", /* bitAnd */
2901 "BIT_OR", /* bitOr */
2902 "AND", /* and */
2903 "OR", /* or */
2904 "NOT", /* not */
2905 "POWER", /* power */
2906 "CONCAT", /* concat */
2907 "ASSIGN", /* assign */
2908 "SUBR_CALL", /* callSubroutine */
2909 "FETCH_RET_VAL", /* fetchRetVal */
2910 "BRANCH", /* branch */
2911 "BRANCH_TRUE", /* branchTrue */
2912 "BRANCH_FALSE", /* branchFalse */
2913 "BRANCH_NEVER", /* branchNever */
2914 "ARRAY_REF", /* arrayRef */
2915 "ARRAY_ASSIGN", /* arrayAssign */
2916 "BEGIN_ARRAY_ITER", /* beginArrayIter */
2917 "ARRAY_ITER", /* arrayIter */
2918 "IN_ARRAY", /* inArray */
2919 "ARRAY_DELETE", /* deleteArrayElement */
2920 "PUSH_ARRAY_SYM", /* pushArraySymVal */
2921 "ARRAY_REF_ASSIGN_SETUP", /* arrayRefAndAssignSetup */
2922 "PUSH_ARG", /* $arg[expr] */
2923 "PUSH_ARG_COUNT", /* $arg[] */
2924 "PUSH_ARG_ARRAY" /* $arg */
2926 int i, j;
2928 printf("\n");
2929 for (i = 0; i < nInstr; ++i) {
2930 printf("Prog %8p ", &inst[i]);
2931 for (j = 0; j < N_OPS; ++j) {
2932 if (inst[i].func == OpFns[j]) {
2933 printf("%22s ", opNames[j]);
2934 if (j == OP_PUSH_SYM || j == OP_ASSIGN) {
2935 Symbol *sym = inst[i+1].sym;
2936 printf("%s", sym->name);
2937 if (sym->value.tag == STRING_TAG &&
2938 strncmp(sym->name, "string #", 8) == 0) {
2939 dumpVal(sym->value);
2941 ++i;
2943 else if (j == OP_BRANCH || j == OP_BRANCH_FALSE ||
2944 j == OP_BRANCH_NEVER || j == OP_BRANCH_TRUE) {
2945 printf("to=(%d) %p", inst[i+1].value,
2946 &inst[i+1] + inst[i+1].value);
2947 ++i;
2949 else if (j == OP_SUBR_CALL) {
2950 printf("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
2951 i += 2;
2953 else if (j == OP_BEGIN_ARRAY_ITER) {
2954 printf("%s in", inst[i+1].sym->name);
2955 ++i;
2957 else if (j == OP_ARRAY_ITER) {
2958 printf("%s = %s++ end-loop=(%d) %p",
2959 inst[i+1].sym->name,
2960 inst[i+2].sym->name,
2961 inst[i+3].value, &inst[i+3] + inst[i+3].value);
2962 i += 3;
2964 else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
2965 j == OP_ARRAY_ASSIGN) {
2966 printf("nDim=%d", inst[i+1].value);
2967 ++i;
2969 else if (j == OP_ARRAY_REF_ASSIGN_SETUP) {
2970 printf("binOp=%s ", inst[i+1].value ? "true" : "false");
2971 printf("nDim=%d", inst[i+2].value);
2972 i += 2;
2974 else if (j == OP_PUSH_ARRAY_SYM) {
2975 printf("%s", inst[++i].sym->name);
2976 printf(" %s", inst[i+1].value ? "createAndRef" : "refOnly");
2977 ++i;
2980 printf("\n");
2981 break;
2984 if (j == N_OPS) {
2985 printf("%x\n", inst[i].value);
2989 #endif /* #ifdef DEBUG_DISASSEMBLER */
2991 #ifdef DEBUG_STACK /* for run-time stack dumping */
2992 #define STACK_DUMP_ARG_PREFIX "Arg"
2993 static void stackdump(int n, int extra)
2995 /* TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
2996 int nArgs = FP_GET_ARG_COUNT(FrameP);
2997 int i, offset;
2998 char buffer[sizeof(STACK_DUMP_ARG_PREFIX) + TYPE_INT_STR_SIZE(int)];
2999 printf("Stack ----->\n");
3000 for (i = 0; i < n + extra; i++) {
3001 char *pos = "";
3002 DataValue *dv = &StackP[-i - 1];
3003 if (dv < TheStack) {
3004 printf("--------------Stack base--------------\n");
3005 break;
3007 offset = dv - FrameP;
3009 printf("%4.4s", i < n ? ">>>>" : "");
3010 printf("%8p ", dv);
3011 switch (offset) {
3012 case 0: pos = "FrameP"; break; /* first local symbol value */
3013 case FP_ARG_ARRAY_CACHE_INDEX: pos = "args"; break; /* arguments array */
3014 case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* number of arguments */
3015 case FP_OLD_FP_INDEX: pos = "OldFP"; break;
3016 case FP_RET_PC_INDEX: pos = "RetPC"; break;
3017 default:
3018 if (offset < -FP_TO_ARGS_DIST && offset >= -FP_TO_ARGS_DIST - nArgs) {
3019 sprintf(pos = buffer, STACK_DUMP_ARG_PREFIX "%d",
3020 offset + FP_TO_ARGS_DIST + nArgs + 1);
3022 break;
3024 printf("%-6s ", pos);
3025 dumpVal(*dv);
3026 printf("\n");
3029 #endif /* ifdef DEBUG_STACK */