1 static const char CVSID
[] = "$Id: interpret.c,v 1.49 2008/03/01 00:27:32 lebert 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 *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
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 *
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 *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
34 #include "interpret.h"
49 #include "../util/VMSparam.h"
52 #include <sys/param.h>
56 #include <X11/Intrinsic.h>
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 */
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);
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);
108 static int bitAnd(void);
109 static int bitOr(void);
110 static int and(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 */
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
;
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 */
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
;
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
;
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)
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)
292 int progLen
, fpOffset
= 0;
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
;
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
);
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";
328 ProgP
->func
= OpFns
[op
];
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";
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";
356 ProgP
->value
= value
;
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";
370 /* Should be ptrdiff_t for branch offsets */
371 ProgP
->value
= to
- ProgP
;
378 ** Return the address at which the next instruction will be stored
386 ** Swap the positions of two contiguous blocks of code. The first block
387 ** running between locations start and boundary, and the second between
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)
417 int AddBreakAddr(Inst
*addr
)
419 if (LoopStackPtr
== LoopStack
) return 1;
421 addr
->value
= NEEDS_BREAK
;
425 int AddContinueAddr(Inst
*addr
)
427 if (LoopStackPtr
== LoopStack
) return 1;
429 addr
->value
= NEEDS_CONTINUE
;
433 static void addLoopAddr(Inst
*addr
)
435 if (LoopStackPtr
> &LoopStack
[LOOP_STACK_SIZE
-1]) {
436 fprintf(stderr
, "NEdit: loop stack overflow in macro parser");
439 *LoopStackPtr
++ = addr
;
442 void FillLoopAddrs(Inst
*breakAddr
, Inst
*continueAddr
)
446 if (LoopStackPtr
< LoopStack
) {
447 fprintf(stderr
, "NEdit: internal error (lsu) in macro parser\n");
450 if (*LoopStackPtr
== NULL
)
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
);
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}};
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
;
495 *(context
->stackP
++) = noValue
; /* old FrameP */
497 context
->stackP
->tag
= NO_TAG
; /* nArgs */
498 context
->stackP
->val
.n
= nArgs
;
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
;
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
519 int ContinueMacro(RestartData
*continuation
, DataValue
*result
, char **msg
)
521 register int status
, instCount
= 0;
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
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
);
538 /* Execute an instruction */
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
) {
550 FreeRestartData(continuation
);
551 restoreContext(&oldContext
);
553 } else if (status
== STAT_DONE
) {
556 FreeRestartData(continuation
);
557 restoreContext(&oldContext
);
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 */
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
581 void RunMacroAsSubrCall(Program
*prog
)
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 */
592 StackP
->tag
= NO_TAG
;
593 StackP
->val
.dataval
= FrameP
; /* old FrameP */
596 StackP
->tag
= NO_TAG
; /* nArgs */
600 *(StackP
++) = noValue
; /* cached arg array */
604 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
605 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
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
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)
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)];
674 static int interatorNameIndex
= 0;
676 sprintf(symbolName
, ARRAY_ITER_SYM_PREFIX
"#%d", interatorNameIndex
);
677 ++interatorNameIndex
;
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
)
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
)) {
702 ** install string str in the global symbol table with a string name
704 Symbol
*InstallStringConstSymbol(const char *str
)
706 static int stringConstIndex
= 0;
709 Symbol
*sym
= LookupStringConstSymbol(str
);
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
)
727 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
)
728 if (strcmp(s
->name
, name
) == 0)
730 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
731 if (strcmp(s
->name
, name
) == 0)
737 ** install symbol name in symbol table
739 Symbol
*InstallSymbol(const char *name
, enum symTypes type
, DataValue value
)
743 s
= (Symbol
*)malloc(sizeof(Symbol
));
744 s
->name
= (char *)malloc(strlen(name
)+1); /* +1 for '\0' */
745 strcpy(s
->name
, name
);
748 if (type
== LOCAL_SYM
) {
749 s
->next
= LocalSymList
;
752 s
->next
= GlobalSymList
;
759 ** Promote a symbol from local to global, removing it from the local symbol
762 Symbol
*PromoteToGlobal(Symbol
*sym
)
765 static DataValue noValue
= {NO_TAG
, {0}};
767 if (sym
->type
!= LOCAL_SYM
)
770 /* Remove sym from the local symbol list */
771 if (sym
== LocalSymList
)
772 LocalSymList
= sym
->next
;
774 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
) {
775 if (s
->next
== sym
) {
782 s
= LookupSymbol(sym
->name
);
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;
803 /* Allocate a new string buffer of length chars */
804 char *AllocString(int length
)
808 mem
= XtMalloc(length
+ sizeof(char *) + 1);
809 *((char **)mem
) = AllocatedStrings
;
810 AllocatedStrings
= mem
;
811 #ifdef TRACK_GARBAGE_LEAKS
812 ++numAllocatedStrings
;
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
822 int AllocNString(NString
*string
, int length
)
826 mem
= XtMalloc(length
+ sizeof(char *) + 1);
833 *((char **)mem
) = AllocatedStrings
;
834 AllocatedStrings
= mem
;
835 #ifdef TRACK_GARBAGE_LEAKS
836 ++numAllocatedStrings
;
838 string
->rep
= mem
+ sizeof(char *) + 1;
839 string
->rep
[length
-1] = '\0'; /* forced \0 */
840 string
->len
= length
-1;
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 */
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
862 int AllocNStringNCpy(NString
*string
, const char *s
, int length
)
864 if (!AllocNString(string
, length
+ 1)) /* add extra char for forced \0 */
868 strncpy(string
->rep
, s
, length
);
869 string
->len
= strlen(string
->rep
); /* re-calculate! */
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))
890 strncpy(string
->rep
, s
, length
);
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
;
904 return(&(mem
->data
));
907 static void MarkArrayContentsAsUsed(SparseArrayEntry
*arrayPtr
)
909 SparseArrayEntry
*globalSEUse
;
912 ((SparseArrayEntryWrapper
*)arrayPtr
)->inUse
= 1;
913 for (globalSEUse
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)arrayPtr
);
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
;
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
) {
957 /* Sweep the global symbol list, marking which strings are still
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
) {
976 next
= *((char **)p
);
977 if (*(p
+ sizeof(char *)) != 0) {
978 *((char **)p
) = AllocatedStrings
;
979 AllocatedStrings
= p
;
982 #ifdef TRACK_GARBAGE_LEAKS
983 --numAllocatedStrings
;
989 nextAP
= AllocatedSparseArrayEntries
;
990 AllocatedSparseArrayEntries
= NULL
;
991 while (nextAP
!= NULL
) {
993 nextAP
= (SparseArrayEntryWrapper
*)nextAP
->next
;
994 if (thisAP
->inUse
!= 0) {
995 thisAP
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
996 AllocatedSparseArrayEntries
= thisAP
;
999 #ifdef TRACK_GARBAGE_LEAKS
1000 --numAllocatedSparseArrayElements
;
1002 XtFree((void *)thisAP
);
1006 #ifdef TRACK_GARBAGE_LEAKS
1007 printf("str count = %d\nary count = %d\n", numAllocatedStrings
, numAllocatedSparseArrayElements
);
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
;
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
;
1030 InitiatingWindow
= context
->runWindow
;
1031 FocusWindow
= context
->focusWindow
;
1034 static void freeSymbolTable(Symbol
*symTab
)
1038 while(symTab
!= NULL
) {
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, ""); \
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; \
1069 return(execError("can't convert array to integer", NULL));
1071 #define POP_STRING(string) \
1072 if (StackP == TheStack) \
1073 return execError(StackUnderflowMsg, ""); \
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; \
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; \
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; \
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; \
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; \
1122 #define BINARY_NUMERIC_OPERATION(operator) \
1124 DISASM_RT(PC-1, 1); \
1128 PUSH_INT(n1 operator n2) \
1131 #define UNARY_NUMERIC_OPERATION(operator) \
1133 DISASM_RT(PC-1, 1); \
1136 PUSH_INT(operator n) \
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)
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
) {
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
;
1173 symVal
= FP_GET_ARG_N(FrameP
, argNum
);
1175 } else if (s
->type
== PROC_VALUE_SYM
) {
1177 if (!(s
->value
.val
.subr
)(FocusWindow
, NULL
, 0,
1178 &symVal
, &errMsg
)) {
1179 return execError(errMsg
, s
->name
);
1182 return execError("reading non-variable: %s", s
->name
);
1183 if (symVal
.tag
== NO_TAG
) {
1184 return execError("variable not set: %s", s
->name
);
1192 static int pushArgVal(void)
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
));
1211 static int pushArgCount(void)
1216 PUSH_INT(FP_GET_ARG_COUNT(FrameP
));
1220 static int pushArgArray(void)
1223 DataValue argVal
, *resultArray
;
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
));
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)
1268 initEmpty
= PC
->value
;
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
;
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
);
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)
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
);
1323 return execError("assignment to non-variable: %s", sym
->name
);
1327 if (sym
->type
== LOCAL_SYM
) {
1328 dataPtr
= &FP_GET_SYM_VAL(FrameP
, sym
);
1331 dataPtr
= &sym
->value
;
1336 if (value
.tag
== ARRAY_TAG
) {
1337 ArrayCopy(dataPtr
, &value
);
1346 ** copy the top value of the stack
1347 ** Before: TheStack-> value, next, ...
1348 ** After: TheStack-> value, value, next, ...
1350 static int dupStack(void)
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
;
1380 if (rightVal
.tag
== ARRAY_TAG
) {
1382 if (leftVal
.tag
== ARRAY_TAG
) {
1383 SparseArrayEntry
*leftIter
, *rightIter
;
1384 resultArray
.tag
= ARRAY_TAG
;
1385 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
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
);
1415 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1416 rightIter
= arrayIterateNext(rightIter
);
1418 if (!insertResult
) {
1419 return(execError("array insertion failure", NULL
));
1425 return(execError("can't mix math with arrays and non-arrays", NULL
));
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
1440 ** Before: TheStack-> value2, value1, next, ...
1441 ** After: TheStack-> resValue, next, ...
1443 static int subtract(void)
1445 DataValue leftVal
, rightVal
, resultArray
;
1452 if (rightVal
.tag
== ARRAY_TAG
) {
1454 if (leftVal
.tag
== ARRAY_TAG
) {
1455 SparseArrayEntry
*leftIter
, *rightIter
;
1456 resultArray
.tag
= ARRAY_TAG
;
1457 resultArray
.val
.arrayPtr
= ArrayNew();
1461 leftIter
= arrayIterateFirst(&leftVal
);
1462 rightIter
= arrayIterateFirst(&rightVal
);
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
);
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
));
1491 return(execError("can't mix math with arrays and non-arrays", NULL
));
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)
1526 return execError("division by zero", "");
1532 static int modulo(void)
1542 return execError("modulo by zero", "");
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(--)
1565 BINARY_NUMERIC_OPERATION(>)
1570 BINARY_NUMERIC_OPERATION(<)
1575 BINARY_NUMERIC_OPERATION(>=)
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
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
) {
1606 if (!StringToNum(v1
.val
.str
.rep
, &number
)) {
1610 v1
.val
.n
= number
== v2
.val
.n
;
1613 else if (v2
.tag
== STRING_TAG
&& v1
.tag
== INT_TAG
) {
1615 if (!StringToNum(v2
.val
.str
.rep
, &number
)) {
1619 v1
.val
.n
= number
== v1
.val
.n
;
1623 return(execError("incompatible types to compare", NULL
));
1630 /* negated eq() call */
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
;
1653 if (rightVal
.tag
== ARRAY_TAG
) {
1655 if (leftVal
.tag
== ARRAY_TAG
) {
1656 SparseArrayEntry
*leftIter
, *rightIter
;
1657 resultArray
.tag
= ARRAY_TAG
;
1658 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
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
));
1686 return(execError("can't mix math with arrays and non-arrays", NULL
));
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
1701 ** Before: TheStack-> value2, value1, next, ...
1702 ** After: TheStack-> resValue, next, ...
1704 static int bitOr(void)
1706 DataValue leftVal
, rightVal
, resultArray
;
1713 if (rightVal
.tag
== ARRAY_TAG
) {
1715 if (leftVal
.tag
== ARRAY_TAG
) {
1716 SparseArrayEntry
*leftIter
, *rightIter
;
1717 resultArray
.tag
= ARRAY_TAG
;
1718 resultArray
.val
.arrayPtr
= ArrayNew();
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
);
1738 leftIter
= arrayIterateNext(leftIter
);
1739 rightIter
= arrayIterateNext(rightIter
);
1742 else if (leftIter
) {
1743 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1744 leftIter
= arrayIterateNext(leftIter
);
1747 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1748 rightIter
= arrayIterateNext(rightIter
);
1750 if (!insertResult
) {
1751 return(execError("array insertion failure", NULL
));
1757 return(execError("can't mix math with arrays and non-arrays", NULL
));
1768 static int and(void)
1770 BINARY_NUMERIC_OPERATION(&&)
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)
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) {
1806 /* since we're integer only, nearly all negative exponents result in 0 */
1810 /* allow error to occur */
1811 n3
= (int)pow((double)n1
, (double)n2
);
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);
1820 /* round to nearest integer for positive values*/
1821 n3
= (int)(pow((double)n1
, (double)n2
) + (double)0.5);
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
;
1845 out
= AllocString(len1
+ len2
+ 1);
1846 strncpy(out
, s1
, len1
);
1847 strcpy(&out
[len1
], s2
);
1848 PUSH_STRING(out
, len1
+ len2
)
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
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)
1873 static DataValue noValue
= {NO_TAG
, {0}};
1883 STACKDUMP(nArgs
, 3);
1886 ** If the subroutine is built-in, call the built-in routine
1888 if (sym
->type
== C_FUNCTION_SYM
) {
1891 /* "pop" stack back to the first argument in the call stack */
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
);
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
;
1921 StackP
->tag
= NO_TAG
; /* old FrameP */
1922 StackP
->val
.dataval
= FrameP
;
1925 StackP
->tag
= NO_TAG
; /* nArgs */
1926 StackP
->val
.n
= nArgs
;
1929 *(StackP
++) = noValue
; /* cached arg array */
1932 prog
= (Program
*)sym
->value
.val
.str
.rep
;
1934 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
1935 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
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
;
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
)
2016 static DataValue noValue
= {NO_TAG
, {0}};
2017 DataValue
*newFrameP
;
2021 STACKDUMP(StackP
- FrameP
+ FP_GET_ARG_COUNT(FrameP
) + FP_TO_ARGS_DIST
, 3);
2023 /* return value is on the stack */
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 */
2035 /* pop past function arguments */
2036 StackP
-= (FP_TO_ARGS_DIST
+ nArgs
);
2039 /* push returned value, if requsted */
2046 } else if (PC
->func
== fetchRetVal
) {
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)
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)
2093 addr
= PC
+ PC
->value
;
2100 static int branchFalse(void)
2109 addr
= PC
+ PC
->value
;
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)
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
);
2148 if (srcIter
->value
.tag
== ARRAY_TAG
) {
2152 errNum
= ArrayCopy(&tmpArray
, &srcIter
->value
);
2153 if (errNum
!= STAT_OK
) {
2156 if (!ArrayInsert(dstArray
, srcIter
->key
, &tmpArray
)) {
2157 return(execError("array copy failed", NULL
));
2161 if (!ArrayInsert(dstArray
, srcIter
->key
, &srcIter
->value
)) {
2162 return(execError("array copy failed", NULL
));
2165 srcIter
= arrayIterateNext(srcIter
);
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
)
2181 int sepLen
= strlen(ARRAY_DIM_SEP
);
2185 keyLength
= sepLen
* (nArgs
- 1);
2186 for (i
= nArgs
- 1; i
>= 0; --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
;
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
);
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
);
2212 return(execError("can only index array with string or int.", NULL
));
2216 for (i
= nArgs
- 1; i
>= 0; --i
) {
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();
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();
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
;
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 */
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
);
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
);
2341 rbTreeNode
*nextIter
= rbTreeNext(iter
);
2342 rbTreeDeleteNode((rbTreeNode
*)theArray
->val
.arrayPtr
,
2343 iter
, arrayDisposeNode
);
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
);
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
);
2376 *theValue
= ((SparseArrayEntry
*) foundNode
)->value
;
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
);
2400 ** move iterator to next entry in array
2402 SparseArrayEntry
*arrayIterateNext(SparseArrayEntry
*iterator
)
2404 SparseArrayEntry
*nextPos
;
2406 nextPos
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)iterator
);
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)
2425 DataValue srcArray
, valueItem
;
2426 char *keyString
= NULL
;
2436 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2437 if (errNum
!= STAT_OK
) {
2442 if (srcArray
.tag
== ARRAY_TAG
) {
2443 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2444 return(execError("referenced array value not in array: %s", keyString
));
2450 return(execError("operator [] on non-array", NULL
));
2455 if (srcArray
.tag
== ARRAY_TAG
) {
2456 PUSH_INT(ArraySize(&srcArray
))
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
;
2489 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2490 if (errNum
!= STAT_OK
) {
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
) {
2508 if (ArrayInsert(&dstArray
, keyString
, &srcValue
)) {
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)
2529 DataValue srcArray
, valueItem
, moveExpr
;
2530 char *keyString
= NULL
;
2533 binaryOp
= PC
->value
;
2539 STACKDUMP(nDim
+ 1, 3);
2546 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 1);
2547 if (errNum
!= STAT_OK
) {
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
));
2563 return(execError("operator [] on non-array", NULL
));
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], ...
2579 ** iter is a symbol which gives the position of the iterator value in
2581 ** arrayVal is the data value holding the array in question
2583 static int beginArrayIter(void)
2586 DataValue
*iteratorValPtr
;
2597 if (iterator
->type
== LOCAL_SYM
) {
2598 iteratorValPtr
= &FP_GET_SYM_VAL(FrameP
, iterator
);
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
);
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)
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
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)
2639 DataValue
*iteratorValPtr
;
2640 DataValue
*itemValPtr
;
2641 SparseArrayEntry
*thisEntry
;
2651 branchAddr
= PC
+ PC
->value
;
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
);
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
);
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
);
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
;
2708 if (theArray
.tag
!= ARRAY_TAG
) {
2709 return(execError("operator in on non-array", NULL
));
2712 if (leftArray
.tag
== ARRAY_TAG
) {
2713 SparseArrayEntry
*iter
;
2717 iter
= arrayIterateFirst(&leftArray
);
2718 while (inResult
&& iter
) {
2719 inResult
= inResult
&& ArrayGet(&theArray
, iter
->key
, &theValue
);
2720 iter
= arrayIterateNext(iter
);
2725 if (ArrayGet(&theArray
, keyStr
, &theValue
)) {
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)
2745 char *keyString
= NULL
;
2752 STACKDUMP(nDim
+ 1, 3);
2757 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2758 if (errNum
!= STAT_OK
) {
2764 if (theArray
.tag
== ARRAY_TAG
) {
2766 ArrayDelete(&theArray
, keyString
);
2769 ArrayDeleteAll(&theArray
);
2773 return(execError("attempt to delete from non-array", NULL
));
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
)
2785 return execError("%s argument out of domain", s
);
2786 else if (errno
== ERANGE
)
2787 return execError("%s result out of range", s
);
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
);
2806 int StringToNum(const char *string
, int *number
)
2808 const char *c
= string
;
2810 while (*c
== ' ' || *c
== '\t') {
2813 if (*c
== '+' || *c
== '-') {
2816 while (isdigit((unsigned char)*c
)) {
2819 while (*c
== ' ' || *c
== '\t') {
2823 /* if everything went as expected, we should be at end, but we're not */
2827 if (sscanf(string
, "%d", number
) != 1) {
2828 /* This case is here to support old behavior */
2835 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2836 static void dumpVal(DataValue dv
)
2840 printf("i=%d", dv
.val
.n
);
2846 char *src
= dv
.val
.str
.rep
;
2851 for (k
= 0; src
[k
] && k
< sizeof s
- 1; k
++) {
2852 s
[k
] = isprint(src
[k
]) ? src
[k
] : '?';
2855 printf("s=\"%s\"%s[%d]", s
,
2856 src
[k
] ? "..." : "", strlen(src
));
2865 printf("<no value>");
2868 printf("?%8p", dv
.val
.inst
);
2872 printf("UNKNOWN DATA TAG %d ?%8p", dv
.tag
, dv
.val
.inst
);
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 */
2887 "SUB", /* subtract */
2888 "MUL", /* multiply */
2891 "NEGATE", /* negate */
2892 "INCR", /* increment */
2893 "DECR", /* decrement */
2900 "BIT_AND", /* bitAnd */
2901 "BIT_OR", /* bitOr */
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 */
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
);
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
);
2949 else if (j
== OP_SUBR_CALL
) {
2950 printf("%s (%d arg)", inst
[i
+1].sym
->name
, inst
[i
+2].value
);
2953 else if (j
== OP_BEGIN_ARRAY_ITER
) {
2954 printf("%s in", inst
[i
+1].sym
->name
);
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
);
2964 else if (j
== OP_ARRAY_REF
|| j
== OP_ARRAY_DELETE
||
2965 j
== OP_ARRAY_ASSIGN
) {
2966 printf("nDim=%d", inst
[i
+1].value
);
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
);
2974 else if (j
== OP_PUSH_ARRAY_SYM
) {
2975 printf("%s", inst
[++i
].sym
->name
);
2976 printf(" %s", inst
[i
+1].value
? "createAndRef" : "refOnly");
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
);
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
++) {
3002 DataValue
*dv
= &StackP
[-i
- 1];
3003 if (dv
< TheStack
) {
3004 printf("--------------Stack base--------------\n");
3007 offset
= dv
- FrameP
;
3009 printf("%4.4s", i
< n
? ">>>>" : "");
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;
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);
3024 printf("%-6s ", pos
);
3029 #endif /* ifdef DEBUG_STACK */