1 static const char CVSID
[] = "$Id: interpret.c,v 1.32 2002/12/12 17:25:49 slobasso Exp $";
2 /*******************************************************************************
4 * interpret.c -- Nirvana Editor macro interpreter *
6 * Copyright (C) 1999 Mark Edel *
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 *
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 *
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 *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
33 #include "interpret.h"
48 #include "../util/VMSparam.h"
51 #include <sys/param.h>
55 #include <X11/Intrinsic.h>
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);
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);
102 static int bitAnd(void);
103 static int bitOr(void);
104 static int and(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 */
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
;
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 */
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
;
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
;
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)
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)
270 int progLen
, fpOffset
= 0;
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
;
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
);
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";
306 *ProgP
++ = OpFns
[op
];
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";
319 *ProgP
++ = (Inst
)sym
;
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";
332 *ProgP
++ = (Inst
)value
;
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";
345 *ProgP
= (Inst
)(to
- ProgP
);
352 ** Return the address at which the next instruction will be stored
360 ** Swap the positions of two contiguous blocks of code. The first block
361 ** running between locations start and boundary, and the second between
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)
391 int AddBreakAddr(Inst
*addr
)
393 if (LoopStackPtr
== LoopStack
) return 1;
399 int AddContinueAddr(Inst
*addr
)
401 if (LoopStackPtr
== LoopStack
) return 1;
403 *addr
= NEEDS_CONTINUE
;
407 static void addLoopAddr(Inst
*addr
)
409 if (LoopStackPtr
> &LoopStack
[LOOP_STACK_SIZE
-1]) {
410 fprintf(stderr
, "NEdit: loop stack overflow in macro parser");
413 *LoopStackPtr
++ = addr
;
416 void FillLoopAddrs(Inst
*breakAddr
, Inst
*continueAddr
)
420 if (LoopStackPtr
< LoopStack
) {
421 fprintf(stderr
, "NEdit: internal error (lsu) in macro parser\n");
424 if (*LoopStackPtr
== NULL
)
426 if (**LoopStackPtr
== NEEDS_BREAK
)
427 **(Inst
***)LoopStackPtr
= (Inst
*)(breakAddr
- *LoopStackPtr
);
428 else if (**LoopStackPtr
== NEEDS_CONTINUE
)
429 **(Inst
***)LoopStackPtr
= (Inst
*)(continueAddr
- *LoopStackPtr
);
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}};
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
;
467 *(context
->stackP
++) = noValue
;
468 context
->stackP
->tag
= NO_TAG
;
469 context
->stackP
->val
.n
= nArgs
;
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
;
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
487 int ContinueMacro(RestartData
*continuation
, DataValue
*result
, char **msg
)
489 register int status
, instCount
= 0;
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
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
);
506 /* Execute an instruction */
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
) {
518 FreeRestartData(continuation
);
519 restoreContext(&oldContext
);
521 } else if (status
== STAT_DONE
) {
524 FreeRestartData(continuation
);
525 restoreContext(&oldContext
);
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 */
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
549 void RunMacroAsSubrCall(Program
*prog
)
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
;
559 StackP
->tag
= NO_TAG
;
560 StackP
->val
.dataval
= FrameP
;
562 StackP
->tag
= NO_TAG
;
567 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
568 *(FrameP
+ s
->value
.val
.n
) = noValue
;
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
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)
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)];
636 static int interatorNameIndex
= 0;
638 sprintf(symbolName
, "aryiter #%d", interatorNameIndex
);
639 ++interatorNameIndex
;
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
)
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
)) {
664 ** install string str in the global symbol table with a string name
666 Symbol
*InstallStringConstSymbol(const char *str
)
668 static int stringConstIndex
= 0;
671 Symbol
*sym
= LookupStringConstSymbol(str
);
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
)
689 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
)
690 if (strcmp(s
->name
, name
) == 0)
692 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
693 if (strcmp(s
->name
, name
) == 0)
699 ** install symbol name in symbol table
701 Symbol
*InstallSymbol(const char *name
, int type
, DataValue value
)
705 s
= (Symbol
*)malloc(sizeof(Symbol
));
706 s
->name
= (char *)malloc(strlen(name
)+1); /* +1 for '\0' */
707 strcpy(s
->name
, name
);
710 if (type
== LOCAL_SYM
) {
711 s
->next
= LocalSymList
;
714 s
->next
= GlobalSymList
;
721 ** Promote a symbol from local to global, removing it from the local symbol
724 Symbol
*PromoteToGlobal(Symbol
*sym
)
727 static DataValue noValue
= {NO_TAG
, {0}};
729 if (sym
->type
!= LOCAL_SYM
)
732 /* Remove sym from the local symbol list */
733 if (sym
== LocalSymList
)
734 LocalSymList
= sym
->next
;
736 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
) {
737 if (s
->next
== sym
) {
744 s
= LookupSymbol(sym
->name
);
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;
765 /* Allocate a new string buffer of length chars */
766 char *AllocString(int length
)
770 mem
= XtMalloc(length
+ sizeof(char *) + 1);
771 *((char **)mem
) = AllocatedStrings
;
772 AllocatedStrings
= mem
;
773 #ifdef TRACK_GARBAGE_LEAKS
774 ++numAllocatedStrings
;
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 */
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
;
807 return(&(mem
->data
));
810 static void MarkArrayContentsAsUsed(SparseArrayEntry
*arrayPtr
)
812 SparseArrayEntry
*globalSEUse
;
815 ((SparseArrayEntryWrapper
*)arrayPtr
)->inUse
= 1;
816 for (globalSEUse
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)arrayPtr
);
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
;
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
) {
860 /* Sweep the global symbol list, marking which strings are still
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
) {
879 next
= *((char **)p
);
880 if (*(p
+ sizeof(char *)) != 0) {
881 *((char **)p
) = AllocatedStrings
;
882 AllocatedStrings
= p
;
885 #ifdef TRACK_GARBAGE_LEAKS
886 --numAllocatedStrings
;
892 nextAP
= AllocatedSparseArrayEntries
;
893 AllocatedSparseArrayEntries
= NULL
;
894 while (nextAP
!= NULL
) {
896 nextAP
= (SparseArrayEntryWrapper
*)nextAP
->next
;
897 if (thisAP
->inUse
!= 0) {
898 thisAP
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
899 AllocatedSparseArrayEntries
= thisAP
;
902 #ifdef TRACK_GARBAGE_LEAKS
903 --numAllocatedSparseArrayElements
;
905 XtFree((void *)thisAP
);
909 #ifdef TRACK_GARBAGE_LEAKS
910 printf("str count = %d\nary count = %d\n", numAllocatedStrings
, numAllocatedSparseArrayElements
);
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
;
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
;
933 InitiatingWindow
= context
->runWindow
;
934 FocusWindow
= context
->focusWindow
;
937 static void freeSymbolTable(Symbol
*symTab
)
941 while(symTab
!= NULL
) {
949 #define POP(dataVal) \
950 if (StackP == Stack) \
951 return execError(StackUnderflowMsg, ""); \
954 #define PUSH(dataVal) \
955 if (StackP >= &Stack[STACK_SIZE]) \
956 return execError(StackOverflowMsg, ""); \
959 #define PEEK(dataVal, peekIndex) \
960 dataVal = *(StackP - peekIndex - 1);
962 #define POP_INT(number) \
963 if (StackP == Stack) \
964 return execError(StackUnderflowMsg, ""); \
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; \
972 return(execError("can't convert array to integer", NULL));
974 #define POP_STRING(string) \
975 if (StackP == Stack) \
976 return execError(StackUnderflowMsg, ""); \
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; \
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; \
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; \
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; \
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; \
1024 #define BINARY_NUMERIC_OPERATION(operator) \
1026 DISASM_RT(PC-1, 1); \
1030 PUSH_INT(n1 operator n2) \
1033 #define UNARY_NUMERIC_OPERATION(operator) \
1035 DISASM_RT(PC-1, 1); \
1038 PUSH_INT(operator n) \
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)
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
) {
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
;
1070 *StackP
= *(FrameP
+ argNum
- nArgs
- 3);
1071 } else if (s
->type
== PROC_VALUE_SYM
) {
1074 if (!(s
->value
.val
.subr
)(FocusWindow
, NULL
, 0,
1076 return execError(errMsg
, s
->name
);
1079 return execError("reading non-variable: %s", s
->name
);
1080 if (StackP
->tag
== NO_TAG
)
1081 return execError("variable not set: %s", s
->name
);
1083 if (StackP
>= &Stack
[STACK_SIZE
])
1084 return execError(StackOverflowMsg
, "");
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)
1106 sym
= (Symbol
*)*PC
;
1108 initEmpty
= (int)*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
;
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
);
1131 if (StackP
>= &Stack
[STACK_SIZE
]) {
1132 return execError(StackOverflowMsg
, "");
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)
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
);
1160 return execError("assignment to non-variable: %s", sym
->name
);
1162 if (StackP
== Stack
)
1163 return execError(StackUnderflowMsg
, "");
1165 if (sym
->type
== LOCAL_SYM
)
1166 dataPtr
= (FrameP
+ sym
->value
.val
.n
);
1168 dataPtr
= &sym
->value
;
1169 if (StackP
->tag
== ARRAY_TAG
) {
1170 ArrayCopy(dataPtr
, StackP
);
1179 ** copy the top value of the stack
1180 ** Before: Stack-> value, next, ...
1181 ** After: Stack-> value, value, next, ...
1183 static int dupStack(void)
1188 if (StackP
>= &Stack
[STACK_SIZE
])
1189 return execError(StackOverflowMsg
, "");
1190 *StackP
= *(StackP
- 1);
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
;
1212 if (rightVal
.tag
== ARRAY_TAG
) {
1214 if (leftVal
.tag
== ARRAY_TAG
) {
1215 SparseArrayEntry
*leftIter
, *rightIter
;
1216 resultArray
.tag
= ARRAY_TAG
;
1217 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
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
);
1247 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1248 rightIter
= arrayIterateNext(rightIter
);
1250 if (!insertResult
) {
1251 return(execError("array insertion failure", NULL
));
1257 return(execError("can't mix math with arrays and non-arrays", NULL
));
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
1272 ** Before: Stack-> value2, value1, next, ...
1273 ** After: Stack-> resValue, next, ...
1275 static int subtract(void)
1277 DataValue leftVal
, rightVal
, resultArray
;
1284 if (rightVal
.tag
== ARRAY_TAG
) {
1286 if (leftVal
.tag
== ARRAY_TAG
) {
1287 SparseArrayEntry
*leftIter
, *rightIter
;
1288 resultArray
.tag
= ARRAY_TAG
;
1289 resultArray
.val
.arrayPtr
= ArrayNew();
1293 leftIter
= arrayIterateFirst(&leftVal
);
1294 rightIter
= arrayIterateFirst(&rightVal
);
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
);
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
));
1323 return(execError("can't mix math with arrays and non-arrays", NULL
));
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)
1358 return execError("division by zero", "");
1364 static int modulo(void)
1374 return execError("modulo by zero", "");
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(--)
1397 BINARY_NUMERIC_OPERATION(>)
1402 BINARY_NUMERIC_OPERATION(<)
1407 BINARY_NUMERIC_OPERATION(>=)
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
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
) {
1438 if (!StringToNum(v1
.val
.str
, &number
)) {
1442 v1
.val
.n
= number
== v2
.val
.n
;
1445 else if (v2
.tag
== STRING_TAG
&& v1
.tag
== INT_TAG
) {
1447 if (!StringToNum(v2
.val
.str
, &number
)) {
1451 v1
.val
.n
= number
== v1
.val
.n
;
1455 return(execError("incompatible types to compare", NULL
));
1462 /* negated eq() call */
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
;
1485 if (rightVal
.tag
== ARRAY_TAG
) {
1487 if (leftVal
.tag
== ARRAY_TAG
) {
1488 SparseArrayEntry
*leftIter
, *rightIter
;
1489 resultArray
.tag
= ARRAY_TAG
;
1490 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
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
));
1518 return(execError("can't mix math with arrays and non-arrays", NULL
));
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
1533 ** Before: Stack-> value2, value1, next, ...
1534 ** After: Stack-> resValue, next, ...
1536 static int bitOr(void)
1538 DataValue leftVal
, rightVal
, resultArray
;
1545 if (rightVal
.tag
== ARRAY_TAG
) {
1547 if (leftVal
.tag
== ARRAY_TAG
) {
1548 SparseArrayEntry
*leftIter
, *rightIter
;
1549 resultArray
.tag
= ARRAY_TAG
;
1550 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
1570 leftIter
= arrayIterateNext(leftIter
);
1571 rightIter
= arrayIterateNext(rightIter
);
1574 else if (leftIter
) {
1575 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1576 leftIter
= arrayIterateNext(leftIter
);
1579 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1580 rightIter
= arrayIterateNext(rightIter
);
1582 if (!insertResult
) {
1583 return(execError("array insertion failure", NULL
));
1589 return(execError("can't mix math with arrays and non-arrays", NULL
));
1600 static int and(void)
1602 BINARY_NUMERIC_OPERATION(&&)
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)
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) {
1638 /* since we're integer only, nearly all negative exponents result in 0 */
1642 /* allow error to occur */
1643 n3
= (int)pow((double)n1
, (double)n2
);
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);
1652 /* round to nearest integer for positive values*/
1653 n3
= (int)(pow((double)n1
, (double)n2
) + (double)0.5);
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
;
1677 out
= AllocString(len1
+ len2
+ 1);
1678 strncpy(out
, s1
, len1
);
1679 strcpy(&out
[len1
], s2
);
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
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)
1705 static DataValue noValue
= {NO_TAG
, {0}};
1709 sym
= (Symbol
*)*PC
++;
1713 STACKDUMP(nArgs
, 3);
1715 if (nArgs
> MAX_ARGS
)
1716 return execError("too many arguments to subroutine %s (max 9)",
1720 ** If the subroutine is built-in, call the built-in routine
1722 if (sym
->type
== C_FUNCTION_SYM
) {
1725 /* "pop" stack back to the first argument in the call stack */
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
);
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
;
1753 StackP
->tag
= NO_TAG
;
1754 StackP
->val
.dataval
= FrameP
;
1756 StackP
->tag
= NO_TAG
;
1757 StackP
->val
.n
= nArgs
;
1760 prog
= (Program
*)sym
->value
.val
.str
;
1762 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
1763 *(FrameP
+ s
->value
.val
.n
) = noValue
;
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
;
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
)
1843 static DataValue noValue
= {NO_TAG
, {0}};
1847 STACKDUMP(StackP
- FrameP
+ FrameP
[-1].val
.n
+ 3, 3);
1849 /* return value is on the stack */
1854 /* pop past local variables */
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 */
1865 /* push returned value, if requsted */
1872 } else if (*PC
== fetchRetVal
) {
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)
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)
1919 addr
= PC
+ (int)*PC
;
1926 static int branchFalse(void)
1935 addr
= PC
+ (int)*PC
;
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)
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
);
1974 if (srcIter
->value
.tag
== ARRAY_TAG
) {
1978 errNum
= ArrayCopy(&tmpArray
, &srcIter
->value
);
1979 if (errNum
!= STAT_OK
) {
1982 if (!ArrayInsert(dstArray
, srcIter
->key
, &tmpArray
)) {
1983 return(execError("array copy failed", NULL
));
1987 if (!ArrayInsert(dstArray
, srcIter
->key
, &srcIter
->value
)) {
1988 return(execError("array copy failed", NULL
));
1991 srcIter
= arrayIterateNext(srcIter
);
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
)
2007 int maxIntDigits
= (sizeof(tmpVal
.val
.n
) * 3) + 1;
2008 int sepLen
= strlen(ARRAY_DIM_SEP
);
2012 keyLength
= sepLen
* (nArgs
- 1);
2013 for (i
= nArgs
- 1; i
>= 0; --i
) {
2015 if (tmpVal
.tag
== INT_TAG
) {
2016 keyLength
+= maxIntDigits
;
2018 else if (tmpVal
.tag
== STRING_TAG
) {
2019 keyLength
+= strlen(tmpVal
.val
.str
);
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
);
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
);
2039 return(execError("can only index array with string or int.", NULL
));
2043 for (i
= nArgs
- 1; i
>= 0; --i
) {
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();
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();
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
;
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 */
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
);
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
);
2167 rbTreeNode
*nextIter
= rbTreeNext(iter
);
2168 rbTreeDeleteNode((rbTreeNode
*)theArray
->val
.arrayPtr
,
2169 iter
, arrayDisposeNode
);
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
));
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
);
2203 *theValue
= ((SparseArrayEntry
*)foundNode
)->value
;
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
);
2226 ** move iterator to next entry in array
2228 SparseArrayEntry
*arrayIterateNext(SparseArrayEntry
*iterator
)
2230 SparseArrayEntry
*nextPos
;
2232 nextPos
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)iterator
);
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)
2251 DataValue srcArray
, valueItem
;
2252 char *keyString
= NULL
;
2262 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2263 if (errNum
!= STAT_OK
) {
2268 if (srcArray
.tag
== ARRAY_TAG
) {
2269 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2270 return(execError("referenced array value not in array: %s", keyString
));
2276 return(execError("operator [] on non-array", NULL
));
2281 if (srcArray
.tag
== ARRAY_TAG
) {
2282 PUSH_INT(ArraySize(&srcArray
))
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
;
2315 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2316 if (errNum
!= STAT_OK
) {
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
) {
2334 if (ArrayInsert(&dstArray
, keyString
, &srcValue
)) {
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)
2355 DataValue srcArray
, valueItem
, moveExpr
;
2356 char *keyString
= NULL
;
2359 binaryOp
= (int)*PC
;
2365 STACKDUMP(nDim
+ 1, 3);
2372 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 1);
2373 if (errNum
!= STAT_OK
) {
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
));
2389 return(execError("operator [] on non-array", NULL
));
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], ...
2405 ** iter is a symbol which gives the position of the iterator value in
2407 ** arrayVal is the data value holding the array in question
2409 static int beginArrayIter(void)
2412 DataValue
*iteratorValPtr
;
2418 iterator
= (Symbol
*)*PC
;
2423 if (iterator
->type
== LOCAL_SYM
) {
2424 iteratorValPtr
= (FrameP
+ iterator
->value
.val
.n
);
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
);
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)
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
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)
2465 DataValue
*iteratorValPtr
;
2466 DataValue
*itemValPtr
;
2467 SparseArrayEntry
*thisEntry
;
2473 item
= (Symbol
*)*PC
;
2475 iterator
= (Symbol
*)*PC
;
2477 branchAddr
= PC
+ (int)*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
);
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
);
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
);
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
;
2533 if (theArray
.tag
!= ARRAY_TAG
) {
2534 return(execError("operator in on non-array", NULL
));
2537 if (leftArray
.tag
== ARRAY_TAG
) {
2538 SparseArrayEntry
*iter
;
2542 iter
= arrayIterateFirst(&leftArray
);
2543 while (inResult
&& iter
) {
2544 inResult
= inResult
&& ArrayGet(&theArray
, iter
->key
, &theValue
);
2545 iter
= arrayIterateNext(iter
);
2550 if (ArrayGet(&theArray
, keyStr
, &theValue
)) {
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)
2570 char *keyString
= NULL
;
2577 STACKDUMP(nDim
+ 1, 3);
2582 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2583 if (errNum
!= STAT_OK
) {
2589 if (theArray
.tag
== ARRAY_TAG
) {
2591 ArrayDelete(&theArray
, keyString
);
2594 ArrayDeleteAll(&theArray
);
2598 return(execError("attempt to delete from non-array", NULL
));
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
)
2610 return execError("%s argument out of domain", s
);
2611 else if (errno
== ERANGE
)
2612 return execError("%s result out of range", s
);
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
);
2631 int StringToNum(const char *string
, int *number
)
2633 const char *c
= string
;
2635 while (*c
== ' ' || *c
== '\t') {
2638 if (*c
== '+' || *c
== '-') {
2641 while (isdigit((unsigned char)*c
)) {
2644 while (*c
== ' ' || *c
== '\t') {
2648 /* if everything went as expected, we should be at end, but we're not */
2652 if (sscanf(string
, "%d", number
) != 1) {
2653 /* This case is here to support old behavior */
2660 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2661 static void dumpVal(DataValue dv
)
2665 printf("i=%d", dv
.val
.n
);
2671 char *src
= dv
.val
.str
;
2676 for (k
= 0; src
[k
] && k
< sizeof s
- 1; k
++) {
2677 s
[k
] = isprint(src
[k
]) ? src
[k
] : '?';
2680 printf("s=\"%s\"%s[%d]", s
,
2681 src
[k
] ? "..." : "", strlen(src
));
2690 printf("<no value>");
2693 printf("?%8p", dv
.val
.inst
);
2697 printf("UNKNOWN DATA TAG %d ?%8p", dv
.tag
, dv
.val
.inst
);
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 */
2712 "SUB", /* subtract */
2713 "MUL", /* multiply */
2716 "NEGATE", /* negate */
2717 "INCR", /* increment */
2718 "DECR", /* decrement */
2725 "BIT_AND", /* bitAnd */
2726 "BIT_OR", /* bitOr */
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 */
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
);
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]));
2769 } else if (j
== OP_SUBR_CALL
) {
2770 printf("%s (%d arg)", ((Symbol
*)inst
[i
+1])->name
,
2773 } else if (j
== OP_BEGIN_ARRAY_ITER
) {
2775 ((Symbol
*)inst
[i
+1])->name
);
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
,
2782 (int)(&inst
[i
+3] + (int)inst
[i
+3]));
2784 } else if (j
== OP_ARRAY_REF
|| j
== OP_ARRAY_DELETE
||
2785 j
== OP_ARRAY_ASSIGN
) {
2789 } else if (j
== OP_ARRAY_REF_ASSIGN_SETUP
) {
2791 ((int)inst
[i
+1]) ? "true" : "false");
2795 } else if (j
== OP_PUSH_ARRAY_SYM
) {
2796 printf("%s", ((Symbol
*)inst
[++i
])->name
);
2798 (int)inst
[i
+1] ? "createAndRef" : "refOnly");
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
;
2819 printf("Stack ----->\n");
2820 for (i
= 0; i
< n
+ extra
; i
++) {
2822 DataValue
*dv
= &StackP
[-i
- 1];
2824 printf("--------------Stack base--------------\n");
2827 offset
= dv
- FrameP
;
2829 printf("%4.4s", i
< n
? ">>>>" : "");
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;
2837 if (offset
< -3 && offset
>= -3 - nArgs
) {
2838 sprintf(pos
= buffer
, "Arg%d", offset
+ 4 + nArgs
);
2842 printf("%-6s ", pos
);
2847 #endif /* ifdef DEBUG_STACK */