1 static const char CVSID
[] = "$Id: interpret.c,v 1.43 2004/11/23 14:37:37 tringali 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 */
75 #define NEEDS_BREAK (Inst)1
76 #define NEEDS_CONTINUE (Inst)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
*Stack
; /* 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
++ = OpFns
[op
];
333 ** Add a symbol operand to the current program
335 int AddSym(Symbol
*sym
, char **msg
)
337 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
338 *msg
= "macro too large";
341 *ProgP
++ = (Inst
)sym
;
346 ** Add an immediate value operand to the current program
348 int AddImmediate(void *value
, char **msg
)
350 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
351 *msg
= "macro too large";
354 *ProgP
++ = (Inst
)value
;
359 ** Add a branch offset operand to the current program
361 int AddBranchOffset(Inst
*to
, char **msg
)
363 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
364 *msg
= "macro too large";
367 *ProgP
= (Inst
)(to
- ProgP
);
374 ** Return the address at which the next instruction will be stored
382 ** Swap the positions of two contiguous blocks of code. The first block
383 ** running between locations start and boundary, and the second between
386 void SwapCode(Inst
*start
, Inst
*boundary
, Inst
*end
)
388 #define reverseCode(L, H) \
389 do { register Inst t, *l = L, *h = H - 1; \
390 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
391 /* double-reverse method: reverse elements of both parts then whole lot */
392 /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
393 reverseCode(start
, boundary
); /* 1 */
394 reverseCode(boundary
, end
); /* 2 */
395 reverseCode(start
, end
); /* 3 */
399 ** Maintain a stack to save addresses of branch operations for break and
400 ** continue statements, so they can be filled in once the information
401 ** on where to branch is known.
403 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or
404 ** AddContinueAddr to register the address at which to store the branch
405 ** address for a break or continue statement, and FillLoopAddrs to fill
406 ** in all the addresses and return to the level of the enclosing loop.
408 void StartLoopAddrList(void)
413 int AddBreakAddr(Inst
*addr
)
415 if (LoopStackPtr
== LoopStack
) return 1;
421 int AddContinueAddr(Inst
*addr
)
423 if (LoopStackPtr
== LoopStack
) return 1;
425 *addr
= NEEDS_CONTINUE
;
429 static void addLoopAddr(Inst
*addr
)
431 if (LoopStackPtr
> &LoopStack
[LOOP_STACK_SIZE
-1]) {
432 fprintf(stderr
, "NEdit: loop stack overflow in macro parser");
435 *LoopStackPtr
++ = addr
;
438 void FillLoopAddrs(Inst
*breakAddr
, Inst
*continueAddr
)
442 if (LoopStackPtr
< LoopStack
) {
443 fprintf(stderr
, "NEdit: internal error (lsu) in macro parser\n");
446 if (*LoopStackPtr
== NULL
)
448 if (**LoopStackPtr
== NEEDS_BREAK
)
449 **(Inst
***)LoopStackPtr
= (Inst
*)(breakAddr
- *LoopStackPtr
);
450 else if (**LoopStackPtr
== NEEDS_CONTINUE
)
451 **(Inst
***)LoopStackPtr
= (Inst
*)(continueAddr
- *LoopStackPtr
);
453 fprintf(stderr
, "NEdit: internal error (uat) in macro parser\n");
458 ** Execute a compiled macro, "prog", using the arguments in the array
459 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.
460 ** if MACRO_DONE is returned, the macro completed, and the returned value
461 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the
462 ** macro exceeded its alotted time-slice and scheduled...
464 int ExecuteMacro(WindowInfo
*window
, Program
*prog
, int nArgs
, DataValue
*args
,
465 DataValue
*result
, RestartData
**continuation
, char **msg
)
467 RestartData
*context
;
468 static DataValue noValue
= {NO_TAG
, {0}};
472 /* Create an execution context (a stack, a stack pointer, a frame pointer,
473 and a program counter) which will retain the program state across
474 preemption and resumption of execution */
475 context
= (RestartData
*)XtMalloc(sizeof(RestartData
));
476 context
->stack
= (DataValue
*)XtMalloc(sizeof(DataValue
) * STACK_SIZE
);
477 *continuation
= context
;
478 context
->stackP
= context
->stack
;
479 context
->pc
= prog
->code
;
480 context
->runWindow
= window
;
481 context
->focusWindow
= window
;
483 /* Push arguments and call information onto the stack */
484 for (i
=0; i
<nArgs
; i
++)
485 *(context
->stackP
++) = args
[i
];
487 context
->stackP
->val
.subr
= NULL
; /* return PC */
488 context
->stackP
->tag
= NO_TAG
;
491 *(context
->stackP
++) = noValue
; /* old FrameP */
493 context
->stackP
->tag
= NO_TAG
; /* nArgs */
494 context
->stackP
->val
.n
= nArgs
;
497 *(context
->stackP
++) = noValue
; /* cached arg array */
499 context
->frameP
= context
->stackP
;
501 /* Initialize and make room on the stack for local variables */
502 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
503 FP_GET_SYM_VAL(context
->frameP
, s
) = noValue
;
507 /* Begin execution, return on error or preemption */
508 return ContinueMacro(context
, result
, msg
);
512 ** Continue the execution of a suspended macro whose state is described in
515 int ContinueMacro(RestartData
*continuation
, DataValue
*result
, char **msg
)
517 register int status
, instCount
= 0;
519 RestartData oldContext
;
521 /* To allow macros to be invoked arbitrarily (such as those automatically
522 triggered within smart-indent) within executing macros, this call is
524 saveContext(&oldContext
);
527 ** Execution Loop: Call the succesive routine addresses in the program
528 ** until one returns something other than STAT_OK, then take action
530 restoreContext(continuation
);
534 /* Execute an instruction */
538 /* If error return was not STAT_OK, return to caller */
539 if (status
!= STAT_OK
) {
540 if (status
== STAT_PREEMPT
) {
541 saveContext(continuation
);
542 restoreContext(&oldContext
);
543 return MACRO_PREEMPT
;
544 } else if (status
== STAT_ERROR
) {
546 FreeRestartData(continuation
);
547 restoreContext(&oldContext
);
549 } else if (status
== STAT_DONE
) {
552 FreeRestartData(continuation
);
553 restoreContext(&oldContext
);
558 /* Count instructions executed. If the instruction limit is hit,
559 preempt, store re-start information in continuation and give
560 X, other macros, and other shell scripts a chance to execute */
562 if (instCount
>= INSTRUCTION_LIMIT
) {
563 saveContext(continuation
);
564 restoreContext(&oldContext
);
565 return MACRO_TIME_LIMIT
;
571 ** If a macro is already executing, and requests that another macro be run,
572 ** this can be called instead of ExecuteMacro to run it in the same context
573 ** as if it were a subroutine. This saves the caller from maintaining
574 ** separate contexts, and serializes processing of the two macros without
577 void RunMacroAsSubrCall(Program
*prog
)
580 static DataValue noValue
= {NO_TAG
, {0}};
582 /* See subroutine "callSubroutine" for a description of the stack frame
583 for a subroutine call */
584 StackP
->tag
= NO_TAG
;
585 StackP
->val
.inst
= PC
; /* return PC */
588 StackP
->tag
= NO_TAG
;
589 StackP
->val
.dataval
= FrameP
; /* old FrameP */
592 StackP
->tag
= NO_TAG
; /* nArgs */
596 *(StackP
++) = noValue
; /* cached arg array */
600 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
601 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
606 void FreeRestartData(RestartData
*context
)
608 XtFree((char *)context
->stack
);
609 XtFree((char *)context
);
613 ** Cause a macro in progress to be preempted (called by commands which take
614 ** a long time, or want to return to the event loop. Call ResumeMacroExecution
617 void PreemptMacro(void)
619 PreemptRequest
= True
;
623 ** Reset the return value for a subroutine which caused preemption (this is
624 ** how to return a value from a routine which preempts instead of returning
625 ** a value directly).
627 void ModifyReturnedValue(RestartData
*context
, DataValue dv
)
629 if (*(context
->pc
-1) == fetchRetVal
)
630 *(context
->stackP
-1) = dv
;
634 ** Called within a routine invoked from a macro, returns the window in
635 ** which the macro is executing (where the banner is, not where it is focused)
637 WindowInfo
*MacroRunWindow(void)
639 return InitiatingWindow
;
643 ** Called within a routine invoked from a macro, returns the window to which
644 ** the currently executing macro is focused (the window which macro commands
645 ** modify, not the window from which the macro is being run)
647 WindowInfo
*MacroFocusWindow(void)
653 ** Set the window to which macro subroutines and actions which operate on an
654 ** implied window are directed.
656 void SetMacroFocusWindow(WindowInfo
*window
)
658 FocusWindow
= window
;
662 ** install an array iteration symbol
663 ** it is tagged as an integer but holds an array node pointer
665 #define ARRAY_ITER_SYM_PREFIX "aryiter "
666 Symbol
*InstallIteratorSymbol(void)
668 char symbolName
[sizeof(ARRAY_ITER_SYM_PREFIX
) + TYPE_INT_STR_SIZE(int)];
670 static int interatorNameIndex
= 0;
672 sprintf(symbolName
, ARRAY_ITER_SYM_PREFIX
"#%d", interatorNameIndex
);
673 ++interatorNameIndex
;
675 value
.val
.arrayPtr
= NULL
;
676 return(InstallSymbol(symbolName
, LOCAL_SYM
, value
));
680 ** Lookup a constant string by its value. This allows reuse of string
681 ** constants and fixing a leak in the interpreter.
683 Symbol
*LookupStringConstSymbol(const char *value
)
687 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
) {
688 if (s
->type
== CONST_SYM
&&
689 s
->value
.tag
== STRING_TAG
&&
690 !strcmp(s
->value
.val
.str
.rep
, value
)) {
698 ** install string str in the global symbol table with a string name
700 Symbol
*InstallStringConstSymbol(const char *str
)
702 static int stringConstIndex
= 0;
705 Symbol
*sym
= LookupStringConstSymbol(str
);
710 sprintf(stringName
, "string #%d", stringConstIndex
++);
711 value
.tag
= STRING_TAG
;
712 AllocNStringCpy(&value
.val
.str
, str
);
713 return(InstallSymbol(stringName
, CONST_SYM
, value
));
717 ** find a symbol in the symbol table
719 Symbol
*LookupSymbol(const char *name
)
723 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
)
724 if (strcmp(s
->name
, name
) == 0)
726 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
727 if (strcmp(s
->name
, name
) == 0)
733 ** install symbol name in symbol table
735 Symbol
*InstallSymbol(const char *name
, enum symTypes type
, DataValue value
)
739 s
= (Symbol
*)malloc(sizeof(Symbol
));
740 s
->name
= (char *)malloc(strlen(name
)+1); /* +1 for '\0' */
741 strcpy(s
->name
, name
);
744 if (type
== LOCAL_SYM
) {
745 s
->next
= LocalSymList
;
748 s
->next
= GlobalSymList
;
755 ** Promote a symbol from local to global, removing it from the local symbol
758 Symbol
*PromoteToGlobal(Symbol
*sym
)
761 static DataValue noValue
= {NO_TAG
, {0}};
763 if (sym
->type
!= LOCAL_SYM
)
766 /* Remove sym from the local symbol list */
767 if (sym
== LocalSymList
)
768 LocalSymList
= sym
->next
;
770 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
) {
771 if (s
->next
== sym
) {
778 s
= LookupSymbol(sym
->name
);
781 return InstallSymbol(sym
->name
, GLOBAL_SYM
, noValue
);
785 ** Allocate memory for a string, and keep track of it, such that it
786 ** can be recovered later using GarbageCollectStrings. (A linked list
787 ** of pointers is maintained by threading through the memory behind
788 ** the returned pointers). Length does not include the terminating null
789 ** character, so to allocate space for a string of strlen == n, you must
790 ** use AllocString(n+1).
793 /*#define TRACK_GARBAGE_LEAKS*/
794 #ifdef TRACK_GARBAGE_LEAKS
795 static int numAllocatedStrings
= 0;
796 static int numAllocatedSparseArrayElements
= 0;
799 /* Allocate a new string buffer of length chars */
800 char *AllocString(int length
)
804 mem
= XtMalloc(length
+ sizeof(char *) + 1);
805 *((char **)mem
) = AllocatedStrings
;
806 AllocatedStrings
= mem
;
807 #ifdef TRACK_GARBAGE_LEAKS
808 ++numAllocatedStrings
;
810 return mem
+ sizeof(char *) + 1;
814 * Allocate a new NString buffer of length chars (terminating \0 included),
815 * The buffer length is initialized to length-1 and the terminating \0 is
818 int AllocNString(NString
*string
, int length
)
822 mem
= XtMalloc(length
+ sizeof(char *) + 1);
829 *((char **)mem
) = AllocatedStrings
;
830 AllocatedStrings
= mem
;
831 #ifdef TRACK_GARBAGE_LEAKS
832 ++numAllocatedStrings
;
834 string
->rep
= mem
+ sizeof(char *) + 1;
835 string
->rep
[length
-1] = '\0'; /* forced \0 */
836 string
->len
= length
-1;
840 /* Allocate a new string buffer of length chars, and copy in the string s */
841 char *AllocStringNCpy(const char *s
, int length
)
843 char *p
= AllocString(length
+ 1); /* add extra char for forced \0 */
848 p
[length
] = '\0'; /* forced \0 */
849 return strncpy(p
, s
, length
);
853 * Allocate a new NString buffer of length chars (terminating \0 NOT included),
854 * and copy at most length characters of the given string.
855 * The buffer length is properly set and the buffer is guaranteed to be
858 int AllocNStringNCpy(NString
*string
, const char *s
, int length
)
860 if (!AllocNString(string
, length
+ 1)) /* add extra char for forced \0 */
864 strncpy(string
->rep
, s
, length
);
865 string
->len
= strlen(string
->rep
); /* re-calculate! */
869 /* Allocate a new copy of string s */
870 char *AllocStringCpy(const char *s
)
872 return AllocStringNCpy(s
, s
? strlen(s
) : 0);
876 * Allocate a new NString buffer, containing a copy of the given string.
877 * The length is set to the length of the string and resulting string is
878 * guaranteed to be \0-terminated.
880 int AllocNStringCpy(NString
*string
, const char *s
)
882 size_t length
= s
? strlen(s
) : 0;
883 if (!AllocNString(string
, length
+ 1))
886 strncpy(string
->rep
, s
, length
);
890 static SparseArrayEntry
*allocateSparseArrayEntry(void)
892 SparseArrayEntryWrapper
*mem
;
894 mem
= (SparseArrayEntryWrapper
*)XtMalloc(sizeof(SparseArrayEntryWrapper
));
895 mem
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
896 AllocatedSparseArrayEntries
= mem
;
897 #ifdef TRACK_GARBAGE_LEAKS
898 ++numAllocatedSparseArrayElements
;
900 return(&(mem
->data
));
903 static void MarkArrayContentsAsUsed(SparseArrayEntry
*arrayPtr
)
905 SparseArrayEntry
*globalSEUse
;
908 ((SparseArrayEntryWrapper
*)arrayPtr
)->inUse
= 1;
909 for (globalSEUse
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)arrayPtr
);
911 globalSEUse
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)globalSEUse
)) {
913 ((SparseArrayEntryWrapper
*)globalSEUse
)->inUse
= 1;
914 /* test first because it may be read-only static string */
915 if (!(*(globalSEUse
->key
- 1))) {
916 *(globalSEUse
->key
- 1) = 1;
918 if (globalSEUse
->value
.tag
== STRING_TAG
) {
919 /* test first because it may be read-only static string */
920 if (!(*(globalSEUse
->value
.val
.str
.rep
- 1))) {
921 *(globalSEUse
->value
.val
.str
.rep
- 1) = 1;
924 else if (globalSEUse
->value
.tag
== ARRAY_TAG
) {
925 MarkArrayContentsAsUsed((SparseArrayEntry
*)globalSEUse
->value
.val
.arrayPtr
);
932 ** Collect strings that are no longer referenced from the global symbol
933 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must
934 ** only be run after all macro activity has ceased.
937 void GarbageCollectStrings(void)
939 SparseArrayEntryWrapper
*nextAP
, *thisAP
;
943 /* mark all strings as unreferenced */
944 for (p
= AllocatedStrings
; p
!= NULL
; p
= *((char **)p
)) {
945 *(p
+ sizeof(char *)) = 0;
948 for (thisAP
= AllocatedSparseArrayEntries
;
949 thisAP
!= NULL
; thisAP
= (SparseArrayEntryWrapper
*)thisAP
->next
) {
953 /* Sweep the global symbol list, marking which strings are still
955 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
) {
956 if (s
->value
.tag
== STRING_TAG
) {
957 /* test first because it may be read-only static string */
958 if (!(*(s
->value
.val
.str
.rep
- 1))) {
959 *(s
->value
.val
.str
.rep
- 1) = 1;
962 else if (s
->value
.tag
== ARRAY_TAG
) {
963 MarkArrayContentsAsUsed((SparseArrayEntry
*)s
->value
.val
.arrayPtr
);
967 /* Collect all of the strings which remain unreferenced */
968 next
= AllocatedStrings
;
969 AllocatedStrings
= NULL
;
970 while (next
!= NULL
) {
972 next
= *((char **)p
);
973 if (*(p
+ sizeof(char *)) != 0) {
974 *((char **)p
) = AllocatedStrings
;
975 AllocatedStrings
= p
;
978 #ifdef TRACK_GARBAGE_LEAKS
979 --numAllocatedStrings
;
985 nextAP
= AllocatedSparseArrayEntries
;
986 AllocatedSparseArrayEntries
= NULL
;
987 while (nextAP
!= NULL
) {
989 nextAP
= (SparseArrayEntryWrapper
*)nextAP
->next
;
990 if (thisAP
->inUse
!= 0) {
991 thisAP
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
992 AllocatedSparseArrayEntries
= thisAP
;
995 #ifdef TRACK_GARBAGE_LEAKS
996 --numAllocatedSparseArrayElements
;
998 XtFree((void *)thisAP
);
1002 #ifdef TRACK_GARBAGE_LEAKS
1003 printf("str count = %d\nary count = %d\n", numAllocatedStrings
, numAllocatedSparseArrayElements
);
1008 ** Save and restore execution context to data structure "context"
1010 static void saveContext(RestartData
*context
)
1012 context
->stack
= Stack
;
1013 context
->stackP
= StackP
;
1014 context
->frameP
= FrameP
;
1016 context
->runWindow
= InitiatingWindow
;
1017 context
->focusWindow
= FocusWindow
;
1020 static void restoreContext(RestartData
*context
)
1022 Stack
= context
->stack
;
1023 StackP
= context
->stackP
;
1024 FrameP
= context
->frameP
;
1026 InitiatingWindow
= context
->runWindow
;
1027 FocusWindow
= context
->focusWindow
;
1030 static void freeSymbolTable(Symbol
*symTab
)
1034 while(symTab
!= NULL
) {
1042 #define POP(dataVal) \
1043 if (StackP == Stack) \
1044 return execError(StackUnderflowMsg, ""); \
1045 dataVal = *--StackP;
1047 #define PUSH(dataVal) \
1048 if (StackP >= &Stack[STACK_SIZE]) \
1049 return execError(StackOverflowMsg, ""); \
1050 *StackP++ = dataVal;
1052 #define PEEK(dataVal, peekIndex) \
1053 dataVal = *(StackP - peekIndex - 1);
1055 #define POP_INT(number) \
1056 if (StackP == Stack) \
1057 return execError(StackUnderflowMsg, ""); \
1059 if (StackP->tag == STRING_TAG) { \
1060 if (!StringToNum(StackP->val.str.rep, &number)) \
1061 return execError(StringToNumberMsg, ""); \
1062 } else if (StackP->tag == INT_TAG) \
1063 number = StackP->val.n; \
1065 return(execError("can't convert array to integer", NULL));
1067 #define POP_STRING(string) \
1068 if (StackP == Stack) \
1069 return execError(StackUnderflowMsg, ""); \
1071 if (StackP->tag == INT_TAG) { \
1072 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1073 sprintf(string, "%d", StackP->val.n); \
1074 } else if (StackP->tag == STRING_TAG) \
1075 string = StackP->val.str.rep; \
1077 return(execError("can't convert array to string", NULL));
1079 #define PEEK_STRING(string, peekIndex) \
1080 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1081 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1082 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \
1084 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1085 string = (StackP - peekIndex - 1)->val.str; \
1088 return(execError("can't convert array to string", NULL)); \
1091 #define PEEK_INT(number, peekIndex) \
1092 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1093 if (!StringToNum((StackP - peekIndex - 1)->val.str, &number)) { \
1094 return execError(StringToNumberMsg, ""); \
1096 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1097 number = (StackP - peekIndex - 1)->val.n; \
1100 return(execError("can't convert array to string", NULL)); \
1103 #define PUSH_INT(number) \
1104 if (StackP >= &Stack[STACK_SIZE]) \
1105 return execError(StackOverflowMsg, ""); \
1106 StackP->tag = INT_TAG; \
1107 StackP->val.n = number; \
1110 #define PUSH_STRING(string, length) \
1111 if (StackP >= &Stack[STACK_SIZE]) \
1112 return execError(StackOverflowMsg, ""); \
1113 StackP->tag = STRING_TAG; \
1114 StackP->val.str.rep = string; \
1115 StackP->val.str.len = length; \
1118 #define BINARY_NUMERIC_OPERATION(operator) \
1120 DISASM_RT(PC-1, 1); \
1124 PUSH_INT(n1 operator n2) \
1127 #define UNARY_NUMERIC_OPERATION(operator) \
1129 DISASM_RT(PC-1, 1); \
1132 PUSH_INT(operator n) \
1136 ** copy a symbol's value onto the stack
1137 ** Before: Prog-> [Sym], next, ...
1138 ** Stack-> next, ...
1139 ** After: Prog-> Sym, [next], ...
1140 ** Stack-> [SymValue], next, ...
1142 static int pushSymVal(void)
1150 s
= (Symbol
*)*PC
++;
1151 if (s
->type
== LOCAL_SYM
) {
1152 *StackP
= FP_GET_SYM_VAL(FrameP
, s
);
1153 } else if (s
->type
== GLOBAL_SYM
|| s
->type
== CONST_SYM
) {
1155 } else if (s
->type
== ARG_SYM
) {
1156 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1157 argNum
= s
->value
.val
.n
;
1158 if (argNum
>= nArgs
) {
1159 return execError("referenced undefined argument: %s", s
->name
);
1161 if (argNum
== N_ARGS_ARG_SYM
) {
1162 StackP
->tag
= INT_TAG
;
1163 StackP
->val
.n
= nArgs
;
1166 *StackP
= FP_GET_ARG_N(FrameP
, argNum
);
1168 } else if (s
->type
== PROC_VALUE_SYM
) {
1171 if (!(s
->value
.val
.subr
)(FocusWindow
, NULL
, 0,
1172 &result
, &errMsg
)) {
1173 return execError(errMsg
, s
->name
);
1177 return execError("reading non-variable: %s", s
->name
);
1178 if (StackP
->tag
== NO_TAG
) {
1179 return execError("variable not set: %s", s
->name
);
1182 if (StackP
>= &Stack
[STACK_SIZE
]) {
1183 return execError(StackOverflowMsg
, "");
1188 static int pushArgVal(void)
1197 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1198 if (argNum
>= nArgs
|| argNum
< 0) {
1199 char argStr
[TYPE_INT_STR_SIZE(argNum
)];
1200 sprintf(argStr
, "%d", argNum
+ 1);
1201 return execError("referenced undefined argument: $args[%s]", argStr
);
1203 PUSH(FP_GET_ARG_N(FrameP
, argNum
));
1207 static int pushArgCount(void)
1212 PUSH_INT(FP_GET_ARG_COUNT(FrameP
));
1216 static int pushArgArray(void)
1219 DataValue argVal
, *resultArray
;
1224 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1225 resultArray
= &FP_GET_ARG_ARRAY_CACHE(FrameP
);
1226 if (resultArray
->tag
!= ARRAY_TAG
) {
1227 resultArray
->tag
= ARRAY_TAG
;
1228 resultArray
->val
.arrayPtr
= ArrayNew();
1230 for (argNum
= 0; argNum
< nArgs
; ++argNum
) {
1231 char intStr
[TYPE_INT_STR_SIZE(argNum
)];
1233 sprintf(intStr
, "%d", argNum
+ 1);
1234 argVal
= FP_GET_ARG_N(FrameP
, argNum
);
1235 if (!ArrayInsert(resultArray
, AllocStringCpy(intStr
), &argVal
)) {
1236 return(execError("array insertion failure", NULL
));
1245 ** Push an array (by reference) onto the stack
1246 ** Before: Prog-> [ArraySym], makeEmpty, next, ...
1247 ** Stack-> next, ...
1248 ** After: Prog-> ArraySym, makeEmpty, [next], ...
1249 ** Stack-> [elemValue], next, ...
1250 ** makeEmpty is either true (1) or false (0): if true, and the element is not
1251 ** present in the array, create it.
1253 static int pushArraySymVal(void)
1262 sym
= (Symbol
*)*PC
;
1264 initEmpty
= (int)*PC
;
1267 if (sym
->type
== LOCAL_SYM
) {
1268 dataPtr
= &FP_GET_SYM_VAL(FrameP
, sym
);
1270 else if (sym
->type
== GLOBAL_SYM
) {
1271 dataPtr
= &sym
->value
;
1274 return execError("assigning to non-lvalue array or non-array: %s", sym
->name
);
1277 if (initEmpty
&& dataPtr
->tag
== NO_TAG
) {
1278 dataPtr
->tag
= ARRAY_TAG
;
1279 dataPtr
->val
.arrayPtr
= ArrayNew();
1282 if (dataPtr
->tag
== NO_TAG
) {
1283 return execError("variable not set: %s", sym
->name
);
1289 if (StackP
>= &Stack
[STACK_SIZE
]) {
1290 return execError(StackOverflowMsg
, "");
1296 ** assign top value to next symbol
1298 ** Before: Prog-> [symbol], next, ...
1299 ** Stack-> [value], next, ...
1300 ** After: Prog-> symbol, [next], ...
1301 ** Stack-> next, ...
1303 static int assign(void)
1311 sym
= (Symbol
*)(*PC
++);
1312 if (sym
->type
!= GLOBAL_SYM
&& sym
->type
!= LOCAL_SYM
) {
1313 if (sym
->type
== ARG_SYM
) {
1314 return execError("assignment to function argument: %s", sym
->name
);
1316 else if (sym
->type
== PROC_VALUE_SYM
) {
1317 return execError("assignment to read-only variable: %s", sym
->name
);
1320 return execError("assignment to non-variable: %s", sym
->name
);
1323 if (StackP
== Stack
) {
1324 return execError(StackUnderflowMsg
, "");
1327 if (sym
->type
== LOCAL_SYM
) {
1328 dataPtr
= &FP_GET_SYM_VAL(FrameP
, sym
);
1331 dataPtr
= &sym
->value
;
1333 if (StackP
->tag
== ARRAY_TAG
) {
1334 ArrayCopy(dataPtr
, StackP
);
1343 ** copy the top value of the stack
1344 ** Before: Stack-> value, next, ...
1345 ** After: Stack-> value, value, next, ...
1347 static int dupStack(void)
1352 if (StackP
>= &Stack
[STACK_SIZE
]) {
1353 return execError(StackOverflowMsg
, "");
1355 *StackP
= *(StackP
- 1);
1361 ** if left and right arguments are arrays, then the result is a new array
1362 ** in which all the keys from both the right and left are copied
1363 ** the values from the right array are used in the result array when the
1364 ** keys are the same
1365 ** Before: Stack-> value2, value1, next, ...
1366 ** After: Stack-> resValue, next, ...
1368 static int add(void)
1370 DataValue leftVal
, rightVal
, resultArray
;
1377 if (rightVal
.tag
== ARRAY_TAG
) {
1379 if (leftVal
.tag
== ARRAY_TAG
) {
1380 SparseArrayEntry
*leftIter
, *rightIter
;
1381 resultArray
.tag
= ARRAY_TAG
;
1382 resultArray
.val
.arrayPtr
= ArrayNew();
1386 leftIter
= arrayIterateFirst(&leftVal
);
1387 rightIter
= arrayIterateFirst(&rightVal
);
1388 while (leftIter
|| rightIter
) {
1389 int insertResult
= 1;
1391 if (leftIter
&& rightIter
) {
1392 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1393 if (compareResult
< 0) {
1394 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1395 leftIter
= arrayIterateNext(leftIter
);
1397 else if (compareResult
> 0) {
1398 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1399 rightIter
= arrayIterateNext(rightIter
);
1402 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1403 leftIter
= arrayIterateNext(leftIter
);
1404 rightIter
= arrayIterateNext(rightIter
);
1407 else if (leftIter
) {
1408 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1409 leftIter
= arrayIterateNext(leftIter
);
1412 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1413 rightIter
= arrayIterateNext(rightIter
);
1415 if (!insertResult
) {
1416 return(execError("array insertion failure", NULL
));
1422 return(execError("can't mix math with arrays and non-arrays", NULL
));
1434 ** if left and right arguments are arrays, then the result is a new array
1435 ** in which only the keys which exist in the left array but not in the right
1437 ** Before: Stack-> value2, value1, next, ...
1438 ** After: Stack-> resValue, next, ...
1440 static int subtract(void)
1442 DataValue leftVal
, rightVal
, resultArray
;
1449 if (rightVal
.tag
== ARRAY_TAG
) {
1451 if (leftVal
.tag
== ARRAY_TAG
) {
1452 SparseArrayEntry
*leftIter
, *rightIter
;
1453 resultArray
.tag
= ARRAY_TAG
;
1454 resultArray
.val
.arrayPtr
= ArrayNew();
1458 leftIter
= arrayIterateFirst(&leftVal
);
1459 rightIter
= arrayIterateFirst(&rightVal
);
1461 int insertResult
= 1;
1463 if (leftIter
&& rightIter
) {
1464 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1465 if (compareResult
< 0) {
1466 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1467 leftIter
= arrayIterateNext(leftIter
);
1469 else if (compareResult
> 0) {
1470 rightIter
= arrayIterateNext(rightIter
);
1473 leftIter
= arrayIterateNext(leftIter
);
1474 rightIter
= arrayIterateNext(rightIter
);
1477 else if (leftIter
) {
1478 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1479 leftIter
= arrayIterateNext(leftIter
);
1481 if (!insertResult
) {
1482 return(execError("array insertion failure", NULL
));
1488 return(execError("can't mix math with arrays and non-arrays", NULL
));
1500 ** Other binary operators
1501 ** Before: Stack-> value2, value1, next, ...
1502 ** After: Stack-> resValue, next, ...
1504 ** Other unary operators
1505 ** Before: Stack-> value, next, ...
1506 ** After: Stack-> resValue, next, ...
1508 static int multiply(void)
1510 BINARY_NUMERIC_OPERATION(*)
1513 static int divide(void)
1523 return execError("division by zero", "");
1529 static int modulo(void)
1539 return execError("modulo by zero", "");
1545 static int negate(void)
1547 UNARY_NUMERIC_OPERATION(-)
1550 static int increment(void)
1552 UNARY_NUMERIC_OPERATION(++)
1555 static int decrement(void)
1557 UNARY_NUMERIC_OPERATION(--)
1562 BINARY_NUMERIC_OPERATION(>)
1567 BINARY_NUMERIC_OPERATION(<)
1572 BINARY_NUMERIC_OPERATION(>=)
1577 BINARY_NUMERIC_OPERATION(<=)
1581 ** verify that compares are between integers and/or strings only
1582 ** Before: Stack-> value1, value2, next, ...
1583 ** After: Stack-> resValue, next, ...
1584 ** where resValue is 1 for true, 0 for false
1595 if (v1
.tag
== INT_TAG
&& v2
.tag
== INT_TAG
) {
1596 v1
.val
.n
= v1
.val
.n
== v2
.val
.n
;
1598 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== STRING_TAG
) {
1599 v1
.val
.n
= !strcmp(v1
.val
.str
.rep
, v2
.val
.str
.rep
);
1601 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== INT_TAG
) {
1603 if (!StringToNum(v1
.val
.str
.rep
, &number
)) {
1607 v1
.val
.n
= number
== v2
.val
.n
;
1610 else if (v2
.tag
== STRING_TAG
&& v1
.tag
== INT_TAG
) {
1612 if (!StringToNum(v2
.val
.str
.rep
, &number
)) {
1616 v1
.val
.n
= number
== v1
.val
.n
;
1620 return(execError("incompatible types to compare", NULL
));
1627 /* negated eq() call */
1635 ** if left and right arguments are arrays, then the result is a new array
1636 ** in which only the keys which exist in both the right or left are copied
1637 ** the values from the right array are used in the result array
1638 ** Before: Stack-> value2, value1, next, ...
1639 ** After: Stack-> resValue, next, ...
1641 static int bitAnd(void)
1643 DataValue leftVal
, rightVal
, resultArray
;
1650 if (rightVal
.tag
== ARRAY_TAG
) {
1652 if (leftVal
.tag
== ARRAY_TAG
) {
1653 SparseArrayEntry
*leftIter
, *rightIter
;
1654 resultArray
.tag
= ARRAY_TAG
;
1655 resultArray
.val
.arrayPtr
= ArrayNew();
1659 leftIter
= arrayIterateFirst(&leftVal
);
1660 rightIter
= arrayIterateFirst(&rightVal
);
1661 while (leftIter
&& rightIter
) {
1662 int insertResult
= 1;
1663 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1665 if (compareResult
< 0) {
1666 leftIter
= arrayIterateNext(leftIter
);
1668 else if (compareResult
> 0) {
1669 rightIter
= arrayIterateNext(rightIter
);
1672 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1673 leftIter
= arrayIterateNext(leftIter
);
1674 rightIter
= arrayIterateNext(rightIter
);
1676 if (!insertResult
) {
1677 return(execError("array insertion failure", NULL
));
1683 return(execError("can't mix math with arrays and non-arrays", NULL
));
1695 ** if left and right arguments are arrays, then the result is a new array
1696 ** in which only the keys which exist in either the right or left but not both
1698 ** Before: Stack-> value2, value1, next, ...
1699 ** After: Stack-> resValue, next, ...
1701 static int bitOr(void)
1703 DataValue leftVal
, rightVal
, resultArray
;
1710 if (rightVal
.tag
== ARRAY_TAG
) {
1712 if (leftVal
.tag
== ARRAY_TAG
) {
1713 SparseArrayEntry
*leftIter
, *rightIter
;
1714 resultArray
.tag
= ARRAY_TAG
;
1715 resultArray
.val
.arrayPtr
= ArrayNew();
1719 leftIter
= arrayIterateFirst(&leftVal
);
1720 rightIter
= arrayIterateFirst(&rightVal
);
1721 while (leftIter
|| rightIter
) {
1722 int insertResult
= 1;
1724 if (leftIter
&& rightIter
) {
1725 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1726 if (compareResult
< 0) {
1727 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1728 leftIter
= arrayIterateNext(leftIter
);
1730 else if (compareResult
> 0) {
1731 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1732 rightIter
= arrayIterateNext(rightIter
);
1735 leftIter
= arrayIterateNext(leftIter
);
1736 rightIter
= arrayIterateNext(rightIter
);
1739 else if (leftIter
) {
1740 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1741 leftIter
= arrayIterateNext(leftIter
);
1744 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1745 rightIter
= arrayIterateNext(rightIter
);
1747 if (!insertResult
) {
1748 return(execError("array insertion failure", NULL
));
1754 return(execError("can't mix math with arrays and non-arrays", NULL
));
1765 static int and(void)
1767 BINARY_NUMERIC_OPERATION(&&)
1772 BINARY_NUMERIC_OPERATION(||)
1775 static int not(void)
1777 UNARY_NUMERIC_OPERATION(!)
1781 ** raise one number to the power of another
1782 ** Before: Stack-> raisedBy, number, next, ...
1783 ** After: Stack-> result, next, ...
1785 static int power(void)
1794 /* We need to round to deal with pow() giving results slightly above
1795 or below the real result since it deals with floating point numbers.
1796 Note: We're not really wanting rounded results, we merely
1797 want to deal with this simple issue. So, 2^-2 = .5, but we
1798 don't want to round this to 1. This is mainly intended to deal with
1799 4^2 = 15.999996 and 16.000001.
1801 if (n2
< 0 && n1
!= 1 && n1
!= -1) {
1803 /* since we're integer only, nearly all negative exponents result in 0 */
1807 /* allow error to occur */
1808 n3
= (int)pow((double)n1
, (double)n2
);
1812 if ((n1
< 0) && (n2
& 1)) {
1813 /* round to nearest integer for negative values*/
1814 n3
= (int)(pow((double)n1
, (double)n2
) - (double)0.5);
1817 /* round to nearest integer for positive values*/
1818 n3
= (int)(pow((double)n1
, (double)n2
) + (double)0.5);
1822 return errCheck("exponentiation");
1826 ** concatenate two top items on the stack
1827 ** Before: Stack-> str2, str1, next, ...
1828 ** After: Stack-> result, next, ...
1830 static int concat(void)
1832 char *s1
, *s2
, *out
;
1842 out
= AllocString(len1
+ len2
+ 1);
1843 strncpy(out
, s1
, len1
);
1844 strcpy(&out
[len1
], s2
);
1845 PUSH_STRING(out
, len1
+ len2
)
1850 ** Call a subroutine or function (user defined or built-in). Args are the
1851 ** subroutine's symbol, and the number of arguments which have been pushed
1854 ** For a macro subroutine, the return address, frame pointer, number of
1855 ** arguments and space for local variables are added to the stack, and the
1856 ** PC is set to point to the new function. For a built-in routine, the
1857 ** arguments are popped off the stack, and the routine is just called.
1859 ** Before: Prog-> [subrSym], nArgs, next, ...
1860 ** Stack-> argN-arg1, next, ...
1861 ** After: Prog-> next, ... -- (built-in called subr)
1862 ** Stack-> retVal?, next, ...
1863 ** or: Prog-> (in called)next, ... -- (macro code called subr)
1864 ** Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
1866 static int callSubroutine(void)
1870 static DataValue noValue
= {NO_TAG
, {0}};
1874 sym
= (Symbol
*)*PC
++;
1878 STACKDUMP(nArgs
, 3);
1881 ** If the subroutine is built-in, call the built-in routine
1883 if (sym
->type
== C_FUNCTION_SYM
) {
1886 /* "pop" stack back to the first argument in the call stack */
1889 /* Call the function and check for preemption */
1890 PreemptRequest
= False
;
1891 if (!sym
->value
.val
.subr(FocusWindow
, StackP
,
1892 nArgs
, &result
, &errMsg
))
1893 return execError(errMsg
, sym
->name
);
1894 if (*PC
== fetchRetVal
) {
1895 if (result
.tag
== NO_TAG
) {
1896 return execError("%s does not return a value", sym
->name
);
1901 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1905 ** Call a macro subroutine:
1907 ** Push all of the required information to resume, and make space on the
1908 ** stack for local variables (and initialize them), on top of the argument
1909 ** values which are already there.
1911 if (sym
->type
== MACRO_FUNCTION_SYM
) {
1912 StackP
->tag
= NO_TAG
; /* return PC */
1913 StackP
->val
.inst
= PC
;
1916 StackP
->tag
= NO_TAG
; /* old FrameP */
1917 StackP
->val
.dataval
= FrameP
;
1920 StackP
->tag
= NO_TAG
; /* nArgs */
1921 StackP
->val
.n
= nArgs
;
1924 *(StackP
++) = noValue
; /* cached arg array */
1927 prog
= (Program
*)sym
->value
.val
.str
.rep
;
1929 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
1930 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
1937 ** Call an action routine
1939 if (sym
->type
== ACTION_ROUTINE_SYM
) {
1940 String argList
[MAX_ARGS
];
1941 Cardinal numArgs
= nArgs
;
1942 XKeyEvent key_event
;
1946 /* Create a fake event with a timestamp suitable for actions which need
1947 timestamps, a marker to indicate that the call was from a macro
1948 (to stop shell commands from putting up their own separate banner) */
1949 disp
=XtDisplay(InitiatingWindow
->shell
);
1950 win
=XtWindow(InitiatingWindow
->shell
);
1952 key_event
.type
= KeyPress
;
1953 key_event
.send_event
= MACRO_EVENT_MARKER
;
1954 key_event
.time
=XtLastTimestampProcessed(XtDisplay(InitiatingWindow
->shell
));
1956 /* The following entries are just filled in to avoid problems
1957 in strange cases, like calling "self_insert()" directly from the
1958 macro menu. In fact the display was sufficient to cure this crash. */
1959 key_event
.display
=disp
;
1960 key_event
.window
=key_event
.root
=key_event
.subwindow
=win
;
1962 /* pop arguments off the stack and put them in the argument list */
1963 for (i
=nArgs
-1; i
>=0; i
--) {
1964 POP_STRING(argList
[i
])
1967 /* Call the action routine and check for preemption */
1968 PreemptRequest
= False
;
1969 sym
->value
.val
.xtproc(FocusWindow
->lastFocus
,
1970 (XEvent
*)&key_event
, argList
, &numArgs
);
1971 if (*PC
== fetchRetVal
) {
1972 return execError("%s does not return a value", sym
->name
);
1974 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1977 /* Calling a non subroutine symbol */
1978 return execError("%s is not a function or subroutine", sym
->name
);
1982 ** This should never be executed, returnVal checks for the presence of this
1983 ** instruction at the PC to decide whether to push the function's return
1984 ** value, then skips over it without executing.
1986 static int fetchRetVal(void)
1988 return execError("internal error: frv", NULL
);
1991 /* see comments for returnValOrNone() */
1992 static int returnNoVal(void)
1994 return returnValOrNone(False
);
1996 static int returnVal(void)
1998 return returnValOrNone(True
);
2002 ** Return from a subroutine call
2003 ** Before: Prog-> [next], ...
2004 ** Stack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
2005 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
2006 ** Stack-> retVal?, next, ...
2008 static int returnValOrNone(int valOnStack
)
2011 static DataValue noValue
= {NO_TAG
, {0}};
2012 DataValue
*newFrameP
;
2016 STACKDUMP(StackP
- FrameP
+ FP_GET_ARG_COUNT(FrameP
) + FP_TO_ARGS_DIST
, 3);
2018 /* return value is on the stack */
2023 /* get stored return information */
2024 nArgs
= FP_GET_ARG_COUNT(FrameP
);
2025 newFrameP
= FP_GET_OLD_FP(FrameP
);
2026 PC
= FP_GET_RET_PC(FrameP
);
2028 /* pop past local variables */
2030 /* pop past function arguments */
2031 StackP
-= (FP_TO_ARGS_DIST
+ nArgs
);
2034 /* push returned value, if requsted */
2041 } else if (*PC
== fetchRetVal
) {
2047 "using return value of %s which does not return a value",
2048 ((Symbol
*)*(PC
- 2))->name
);
2052 /* NULL return PC indicates end of program */
2053 return PC
== NULL
? STAT_DONE
: STAT_OK
;
2057 ** Unconditional branch offset by immediate operand
2059 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2060 ** After: Prog-> branchDest, next, ..., (branchdest)[next]
2062 static int branch(void)
2072 ** Conditional branches if stack value is True/False (non-zero/0) to address
2073 ** of immediate operand (pops stack)
2075 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2076 ** After: either: Prog-> branchDest, [next], ...
2077 ** After: or: Prog-> branchDest, next, ..., (branchdest)[next]
2079 static int branchTrue(void)
2088 addr
= PC
+ (int)*PC
;
2095 static int branchFalse(void)
2104 addr
= PC
+ (int)*PC
;
2113 ** Ignore the address following the instruction and continue. Why? So
2114 ** some code that uses conditional branching doesn't have to figure out
2115 ** whether to store a branch address.
2117 ** Before: Prog-> [branchDest], next, ...
2118 ** After: Prog-> branchDest, [next], ...
2120 static int branchNever(void)
2130 ** recursively copy(duplicate) the sparse array nodes of an array
2131 ** this does not duplicate the key/node data since they are never
2132 ** modified, only replaced
2134 int ArrayCopy(DataValue
*dstArray
, DataValue
*srcArray
)
2136 SparseArrayEntry
*srcIter
;
2138 dstArray
->tag
= ARRAY_TAG
;
2139 dstArray
->val
.arrayPtr
= ArrayNew();
2141 srcIter
= arrayIterateFirst(srcArray
);
2143 if (srcIter
->value
.tag
== ARRAY_TAG
) {
2147 errNum
= ArrayCopy(&tmpArray
, &srcIter
->value
);
2148 if (errNum
!= STAT_OK
) {
2151 if (!ArrayInsert(dstArray
, srcIter
->key
, &tmpArray
)) {
2152 return(execError("array copy failed", NULL
));
2156 if (!ArrayInsert(dstArray
, srcIter
->key
, &srcIter
->value
)) {
2157 return(execError("array copy failed", NULL
));
2160 srcIter
= arrayIterateNext(srcIter
);
2166 ** creates an allocated string of a single key for all the sub-scripts
2167 ** using ARRAY_DIM_SEP as a separator
2168 ** this function uses the PEEK macros in order to remove most limits on
2169 ** the number of arguments to an array
2170 ** I really need to optimize the size approximation rather than assuming
2171 ** a worst case size for every integer argument
2173 static int makeArrayKeyFromArgs(int nArgs
, char **keyString
, int leaveParams
)
2176 int sepLen
= strlen(ARRAY_DIM_SEP
);
2180 keyLength
= sepLen
* (nArgs
- 1);
2181 for (i
= nArgs
- 1; i
>= 0; --i
) {
2183 if (tmpVal
.tag
== INT_TAG
) {
2184 keyLength
+= TYPE_INT_STR_SIZE(tmpVal
.val
.n
);
2186 else if (tmpVal
.tag
== STRING_TAG
) {
2187 keyLength
+= tmpVal
.val
.str
.len
;
2190 return(execError("can only index array with string or int.", NULL
));
2193 *keyString
= AllocString(keyLength
+ 1);
2194 (*keyString
)[0] = 0;
2195 for (i
= nArgs
- 1; i
>= 0; --i
) {
2196 if (i
!= nArgs
- 1) {
2197 strcat(*keyString
, ARRAY_DIM_SEP
);
2200 if (tmpVal
.tag
== INT_TAG
) {
2201 sprintf(&((*keyString
)[strlen(*keyString
)]), "%d", tmpVal
.val
.n
);
2203 else if (tmpVal
.tag
== STRING_TAG
) {
2204 strcat(*keyString
, tmpVal
.val
.str
.rep
);
2207 return(execError("can only index array with string or int.", NULL
));
2211 for (i
= nArgs
- 1; i
>= 0; --i
) {
2219 ** allocate an empty array node, this is used as the root node and never
2220 ** contains any data, only refernces to other nodes
2222 static rbTreeNode
*arrayEmptyAllocator(void)
2224 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
2226 newNode
->key
= NULL
;
2227 newNode
->value
.tag
= NO_TAG
;
2229 return((rbTreeNode
*)newNode
);
2233 ** create and copy array node and copy contents, we merely copy pointers
2234 ** since they are never modified, only replaced
2236 static rbTreeNode
*arrayAllocateNode(rbTreeNode
*src
)
2238 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
2240 newNode
->key
= ((SparseArrayEntry
*)src
)->key
;
2241 newNode
->value
= ((SparseArrayEntry
*)src
)->value
;
2243 return((rbTreeNode
*)newNode
);
2247 ** copy array node data, we merely copy pointers since they are never
2248 ** modified, only replaced
2250 static int arrayEntryCopyToNode(rbTreeNode
*dst
, rbTreeNode
*src
)
2252 ((SparseArrayEntry
*)dst
)->key
= ((SparseArrayEntry
*)src
)->key
;
2253 ((SparseArrayEntry
*)dst
)->value
= ((SparseArrayEntry
*)src
)->value
;
2258 ** compare two array nodes returning an integer value similar to strcmp()
2260 static int arrayEntryCompare(rbTreeNode
*left
, rbTreeNode
*right
)
2262 return(strcmp(((SparseArrayEntry
*)left
)->key
, ((SparseArrayEntry
*)right
)->key
));
2266 ** dispose an array node, garbage collection handles this, so we mark it
2267 ** to allow iterators in macro language to determine they have been unlinked
2269 static void arrayDisposeNode(rbTreeNode
*src
)
2271 /* Let garbage collection handle this but mark it so iterators can tell */
2278 struct SparseArrayEntry
*ArrayNew(void)
2280 return((struct SparseArrayEntry
*)rbTreeNew(arrayEmptyAllocator
));
2284 ** insert a DataValue into an array, allocate the array if needed
2285 ** keyStr must be a string that was allocated with AllocString()
2287 int ArrayInsert(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
2289 SparseArrayEntry tmpEntry
;
2290 rbTreeNode
*insertedNode
;
2292 tmpEntry
.key
= keyStr
;
2293 tmpEntry
.value
= *theValue
;
2295 if (theArray
->val
.arrayPtr
== NULL
) {
2296 theArray
->val
.arrayPtr
= ArrayNew();
2298 if (theArray
->val
.arrayPtr
!= NULL
) {
2299 insertedNode
= rbTreeInsert((rbTreeNode
*)(theArray
->val
.arrayPtr
),
2300 (rbTreeNode
*)&tmpEntry
,
2301 arrayEntryCompare
, arrayAllocateNode
, arrayEntryCopyToNode
);
2313 ** remove a node from an array whose key matches keyStr
2315 void ArrayDelete(DataValue
*theArray
, char *keyStr
)
2317 SparseArrayEntry searchEntry
;
2319 if (theArray
->val
.arrayPtr
) {
2320 searchEntry
.key
= keyStr
;
2321 rbTreeDelete((rbTreeNode
*)theArray
->val
.arrayPtr
, (rbTreeNode
*)&searchEntry
,
2322 arrayEntryCompare
, arrayDisposeNode
);
2327 ** remove all nodes from an array
2329 void ArrayDeleteAll(DataValue
*theArray
)
2332 if (theArray
->val
.arrayPtr
) {
2333 rbTreeNode
*iter
= rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
2335 rbTreeNode
*nextIter
= rbTreeNext(iter
);
2336 rbTreeDeleteNode((rbTreeNode
*)theArray
->val
.arrayPtr
,
2337 iter
, arrayDisposeNode
);
2345 ** returns the number of elements (nodes containing values) of an array
2347 int ArraySize(DataValue
*theArray
)
2349 if (theArray
->val
.arrayPtr
) {
2350 return(rbTreeSize((rbTreeNode
*)theArray
->val
.arrayPtr
));
2358 ** retrieves an array node whose key matches
2359 ** returns 1 for success 0 for not found
2361 int ArrayGet(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
2363 SparseArrayEntry searchEntry
;
2364 rbTreeNode
*foundNode
;
2366 if (theArray
->val
.arrayPtr
) {
2367 searchEntry
.key
= keyStr
;
2368 foundNode
= rbTreeFind((rbTreeNode
*)theArray
->val
.arrayPtr
,
2369 (rbTreeNode
*)&searchEntry
, arrayEntryCompare
);
2371 *theValue
= ((SparseArrayEntry
*)foundNode
)->value
;
2379 ** get pointer to start iterating an array
2381 SparseArrayEntry
*arrayIterateFirst(DataValue
*theArray
)
2383 SparseArrayEntry
*startPos
;
2384 if (theArray
->val
.arrayPtr
) {
2385 startPos
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
2394 ** move iterator to next entry in array
2396 SparseArrayEntry
*arrayIterateNext(SparseArrayEntry
*iterator
)
2398 SparseArrayEntry
*nextPos
;
2400 nextPos
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)iterator
);
2409 ** evaluate an array element and push the result onto the stack
2411 ** Before: Prog-> [nDim], next, ...
2412 ** Stack-> indnDim, ... ind1, ArraySym, next, ...
2413 ** After: Prog-> nDim, [next], ...
2414 ** Stack-> indexedArrayVal, next, ...
2416 static int arrayRef(void)
2419 DataValue srcArray
, valueItem
;
2420 char *keyString
= NULL
;
2430 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2431 if (errNum
!= STAT_OK
) {
2436 if (srcArray
.tag
== ARRAY_TAG
) {
2437 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2438 return(execError("referenced array value not in array: %s", keyString
));
2444 return(execError("operator [] on non-array", NULL
));
2449 if (srcArray
.tag
== ARRAY_TAG
) {
2450 PUSH_INT(ArraySize(&srcArray
))
2454 return(execError("operator [] on non-array", NULL
));
2460 ** assign to an array element of a referenced array on the stack
2462 ** Before: Prog-> [nDim], next, ...
2463 ** Stack-> rhs, indnDim, ... ind1, ArraySym, next, ...
2464 ** After: Prog-> nDim, [next], ...
2465 ** Stack-> next, ...
2467 static int arrayAssign(void)
2469 char *keyString
= NULL
;
2470 DataValue srcValue
, dstArray
;
2483 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2484 if (errNum
!= STAT_OK
) {
2490 if (dstArray
.tag
!= ARRAY_TAG
&& dstArray
.tag
!= NO_TAG
) {
2491 return(execError("cannot assign array element of non-array", NULL
));
2493 if (srcValue
.tag
== ARRAY_TAG
) {
2494 DataValue arrayCopyValue
;
2496 errNum
= ArrayCopy(&arrayCopyValue
, &srcValue
);
2497 srcValue
= arrayCopyValue
;
2498 if (errNum
!= STAT_OK
) {
2502 if (ArrayInsert(&dstArray
, keyString
, &srcValue
)) {
2506 return(execError("array member allocation failure", NULL
));
2509 return(execError("empty operator []", NULL
));
2513 ** for use with assign-op operators (eg a[i,j] += k
2515 ** Before: Prog-> [binOp], nDim, next, ...
2516 ** Stack-> [rhs], indnDim, ... ind1, next, ...
2517 ** After: Prog-> binOp, nDim, [next], ...
2518 ** Stack-> [rhs], arrayValue, next, ...
2520 static int arrayRefAndAssignSetup(void)
2523 DataValue srcArray
, valueItem
, moveExpr
;
2524 char *keyString
= NULL
;
2527 binaryOp
= (int)*PC
;
2533 STACKDUMP(nDim
+ 1, 3);
2540 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 1);
2541 if (errNum
!= STAT_OK
) {
2545 PEEK(srcArray
, nDim
)
2546 if (srcArray
.tag
== ARRAY_TAG
) {
2547 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2548 return(execError("referenced array value not in array: %s", keyString
));
2557 return(execError("operator [] on non-array", NULL
));
2561 return(execError("array[] not an lvalue", NULL
));
2566 ** setup symbol values for array iteration in interpreter
2568 ** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ...
2569 ** Stack-> [arrayVal], next, ...
2570 ** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ...
2571 ** Stack-> [next], ...
2573 ** iter is a symbol which gives the position of the iterator value in
2575 ** arrayVal is the data value holding the array in question
2577 static int beginArrayIter(void)
2580 DataValue
*iteratorValPtr
;
2586 iterator
= (Symbol
*)*PC
;
2591 if (iterator
->type
== LOCAL_SYM
) {
2592 iteratorValPtr
= &FP_GET_SYM_VAL(FrameP
, iterator
);
2595 return(execError("bad temporary iterator: %s", iterator
->name
));
2598 iteratorValPtr
->tag
= INT_TAG
;
2599 if (arrayVal
.tag
!= ARRAY_TAG
) {
2600 return(execError("can't iterate non-array", NULL
));
2603 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateFirst(&arrayVal
);
2608 ** copy key to symbol if node is still valid, marked bad by a color of -1
2609 ** then move iterator to next node
2610 ** this allows iterators to progress even if you delete any node in the array
2611 ** except the item just after the current key
2613 ** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ...
2614 ** Stack-> [next], ...
2615 ** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ...
2616 ** Stack-> [next], ... (unchanged)
2618 ** iter is a symbol which gives the position of the iterator value in
2619 ** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers
2620 ** to the array and a position within it
2621 ** iterVal is the programmer-visible symbol which will take the current
2623 ** endLoopBranch is the instruction offset to the instruction following the
2624 ** loop (measured from itself)
2625 ** arrayVal is the data value holding the array in question
2626 ** The return-to-start-of-loop branch (at the end of the loop) should address
2627 ** the ARRAY_ITER instruction
2629 static int arrayIter(void)
2633 DataValue
*iteratorValPtr
;
2634 DataValue
*itemValPtr
;
2635 SparseArrayEntry
*thisEntry
;
2641 item
= (Symbol
*)*PC
;
2643 iterator
= (Symbol
*)*PC
;
2645 branchAddr
= PC
+ (int)*PC
;
2648 if (item
->type
== LOCAL_SYM
) {
2649 itemValPtr
= &FP_GET_SYM_VAL(FrameP
, item
);
2651 else if (item
->type
== GLOBAL_SYM
) {
2652 itemValPtr
= &(item
->value
);
2655 return(execError("can't assign to: %s", item
->name
));
2657 itemValPtr
->tag
= NO_TAG
;
2659 if (iterator
->type
== LOCAL_SYM
) {
2660 iteratorValPtr
= &FP_GET_SYM_VAL(FrameP
, iterator
);
2663 return(execError("bad temporary iterator: %s", iterator
->name
));
2666 thisEntry
= (SparseArrayEntry
*)iteratorValPtr
->val
.arrayPtr
;
2667 if (thisEntry
&& thisEntry
->nodePtrs
.color
!= -1) {
2668 itemValPtr
->tag
= STRING_TAG
;
2669 itemValPtr
->val
.str
.rep
= thisEntry
->key
;
2670 itemValPtr
->val
.str
.len
= strlen(thisEntry
->key
);
2672 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateNext(thisEntry
);
2681 ** determine if a key or keys exists in an array
2682 ** if the left argument is a string or integer a single check is performed
2683 ** if the key exists, 1 is pushed onto the stack, otherwise 0
2684 ** if the left argument is an array 1 is pushed onto the stack if every key
2685 ** in the left array exists in the right array, otherwise 0
2687 ** Before: Prog-> [next], ...
2688 ** Stack-> [ArraySym], inSymbol, next, ...
2689 ** After: Prog-> [next], ... -- (unchanged)
2690 ** Stack-> next, ...
2692 static int inArray(void)
2694 DataValue theArray
, leftArray
, theValue
;
2702 if (theArray
.tag
!= ARRAY_TAG
) {
2703 return(execError("operator in on non-array", NULL
));
2706 if (leftArray
.tag
== ARRAY_TAG
) {
2707 SparseArrayEntry
*iter
;
2711 iter
= arrayIterateFirst(&leftArray
);
2712 while (inResult
&& iter
) {
2713 inResult
= inResult
&& ArrayGet(&theArray
, iter
->key
, &theValue
);
2714 iter
= arrayIterateNext(iter
);
2719 if (ArrayGet(&theArray
, keyStr
, &theValue
)) {
2728 ** remove a given key from an array unless nDim is 0, then all keys are removed
2730 ** for use with assign-op operators (eg a[i,j] += k
2731 ** Before: Prog-> [nDim], next, ...
2732 ** Stack-> [indnDim], ... ind1, arrayValue, next, ...
2733 ** After: Prog-> nDim, [next], ...
2734 ** Stack-> next, ...
2736 static int deleteArrayElement(void)
2739 char *keyString
= NULL
;
2746 STACKDUMP(nDim
+ 1, 3);
2751 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2752 if (errNum
!= STAT_OK
) {
2758 if (theArray
.tag
== ARRAY_TAG
) {
2760 ArrayDelete(&theArray
, keyString
);
2763 ArrayDeleteAll(&theArray
);
2767 return(execError("attempt to delete from non-array", NULL
));
2773 ** checks errno after operations which can set it. If an error occured,
2774 ** creates appropriate error messages and returns false
2776 static int errCheck(const char *s
)
2779 return execError("%s argument out of domain", s
);
2780 else if (errno
== ERANGE
)
2781 return execError("%s result out of range", s
);
2787 ** combine two strings in a static area and set ErrMsg to point to the
2788 ** result. Returns false so a single return execError() statement can
2789 ** be used to both process the message and return.
2791 static int execError(const char *s1
, const char *s2
)
2793 static char msg
[MAX_ERR_MSG_LEN
];
2795 sprintf(msg
, s1
, s2
);
2800 int StringToNum(const char *string
, int *number
)
2802 const char *c
= string
;
2804 while (*c
== ' ' || *c
== '\t') {
2807 if (*c
== '+' || *c
== '-') {
2810 while (isdigit((unsigned char)*c
)) {
2813 while (*c
== ' ' || *c
== '\t') {
2817 /* if everything went as expected, we should be at end, but we're not */
2821 if (sscanf(string
, "%d", number
) != 1) {
2822 /* This case is here to support old behavior */
2829 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2830 static void dumpVal(DataValue dv
)
2834 printf("i=%d", dv
.val
.n
);
2840 char *src
= dv
.val
.str
.rep
;
2845 for (k
= 0; src
[k
] && k
< sizeof s
- 1; k
++) {
2846 s
[k
] = isprint(src
[k
]) ? src
[k
] : '?';
2849 printf("s=\"%s\"%s[%d]", s
,
2850 src
[k
] ? "..." : "", strlen(src
));
2859 printf("<no value>");
2862 printf("?%8p", dv
.val
.inst
);
2866 printf("UNKNOWN DATA TAG %d ?%8p", dv
.tag
, dv
.val
.inst
);
2870 #endif /* #ifdef DEBUG_DISASSEMBLER */
2872 #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */
2873 static void disasm(Inst
*inst
, int nInstr
)
2875 static const char *opNames
[N_OPS
] = {
2876 "RETURN_NO_VAL", /* returnNoVal */
2877 "RETURN", /* returnVal */
2878 "PUSH_SYM", /* pushSymVal */
2879 "DUP", /* dupStack */
2881 "SUB", /* subtract */
2882 "MUL", /* multiply */
2885 "NEGATE", /* negate */
2886 "INCR", /* increment */
2887 "DECR", /* decrement */
2894 "BIT_AND", /* bitAnd */
2895 "BIT_OR", /* bitOr */
2899 "POWER", /* power */
2900 "CONCAT", /* concat */
2901 "ASSIGN", /* assign */
2902 "SUBR_CALL", /* callSubroutine */
2903 "FETCH_RET_VAL", /* fetchRetVal */
2904 "BRANCH", /* branch */
2905 "BRANCH_TRUE", /* branchTrue */
2906 "BRANCH_FALSE", /* branchFalse */
2907 "BRANCH_NEVER", /* branchNever */
2908 "ARRAY_REF", /* arrayRef */
2909 "ARRAY_ASSIGN", /* arrayAssign */
2910 "BEGIN_ARRAY_ITER", /* beginArrayIter */
2911 "ARRAY_ITER", /* arrayIter */
2912 "IN_ARRAY", /* inArray */
2913 "ARRAY_DELETE", /* deleteArrayElement */
2914 "PUSH_ARRAY_SYM", /* pushArraySymVal */
2915 "ARRAY_REF_ASSIGN_SETUP", /* arrayRefAndAssignSetup */
2916 "PUSH_ARG", /* $arg[expr] */
2917 "PUSH_ARG_COUNT", /* $arg[] */
2918 "PUSH_ARG_ARRAY" /* $arg */
2923 for (i
= 0; i
< nInstr
; ++i
) {
2924 printf("Prog %8p ", &inst
[i
]);
2925 for (j
= 0; j
< N_OPS
; ++j
) {
2926 if (inst
[i
] == OpFns
[j
]) {
2927 printf("%22s ", opNames
[j
]);
2928 if (j
== OP_PUSH_SYM
|| j
== OP_ASSIGN
) {
2929 Symbol
*sym
= (Symbol
*)inst
[i
+1];
2930 printf("%s", sym
->name
);
2931 if (sym
->value
.tag
== STRING_TAG
&&
2932 strncmp(sym
->name
, "string #", 8) == 0) {
2933 dumpVal(sym
->value
);
2937 else if (j
== OP_BRANCH
|| j
== OP_BRANCH_FALSE
||
2938 j
== OP_BRANCH_NEVER
|| j
== OP_BRANCH_TRUE
) {
2939 printf("to=(%d) %x", (int)inst
[i
+1],
2940 (int)(&inst
[i
+1] + (int)inst
[i
+1]));
2943 else if (j
== OP_SUBR_CALL
) {
2944 printf("%s (%d arg)", ((Symbol
*)inst
[i
+1])->name
,
2948 else if (j
== OP_BEGIN_ARRAY_ITER
) {
2950 ((Symbol
*)inst
[i
+1])->name
);
2953 else if (j
== OP_ARRAY_ITER
) {
2954 printf("%s = %s++ end-loop=(%d) %x",
2955 ((Symbol
*)inst
[i
+1])->name
,
2956 ((Symbol
*)inst
[i
+2])->name
,
2958 (int)(&inst
[i
+3] + (int)inst
[i
+3]));
2961 else if (j
== OP_ARRAY_REF
|| j
== OP_ARRAY_DELETE
||
2962 j
== OP_ARRAY_ASSIGN
) {
2967 else if (j
== OP_ARRAY_REF_ASSIGN_SETUP
) {
2969 ((int)inst
[i
+1]) ? "true" : "false");
2974 else if (j
== OP_PUSH_ARRAY_SYM
) {
2975 printf("%s", ((Symbol
*)inst
[++i
])->name
);
2977 (int)inst
[i
+1] ? "createAndRef" : "refOnly");
2986 printf("%x\n", (int)inst
[i
]);
2990 #endif /* #ifdef DEBUG_DISASSEMBLER */
2992 #ifdef DEBUG_STACK /* for run-time stack dumping */
2993 #define STACK_DUMP_ARG_PREFIX "Arg"
2994 static void stackdump(int n
, int extra
)
2996 /* Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
2997 int nArgs
= FP_GET_ARG_COUNT(FrameP
);
2999 char buffer
[sizeof(STACK_DUMP_ARG_PREFIX
) + TYPE_INT_STR_SIZE(int)];
3000 printf("Stack ----->\n");
3001 for (i
= 0; i
< n
+ extra
; i
++) {
3003 DataValue
*dv
= &StackP
[-i
- 1];
3005 printf("--------------Stack base--------------\n");
3008 offset
= dv
- FrameP
;
3010 printf("%4.4s", i
< n
? ">>>>" : "");
3013 case 0: pos
= "FrameP"; break; /* first local symbol value */
3014 case FP_ARG_ARRAY_CACHE_INDEX
: pos
= "args"; break; /* number of arguments */
3015 case FP_ARG_COUNT_INDEX
: pos
= "NArgs"; break; /* number of arguments */
3016 case FP_OLD_FP_INDEX
: pos
= "OldFP"; break;
3017 case FP_RET_PC_INDEX
: pos
= "RetPC"; break;
3019 if (offset
< -FP_TO_ARGS_DIST
&& offset
>= -FP_TO_ARGS_DIST
- nArgs
) {
3020 sprintf(pos
= buffer
, STACK_DUMP_ARG_PREFIX
"%d", offset
+ FP_TO_ARGS_DIST
+ nArgs
+ 1);
3024 printf("%-6s ", pos
);
3029 #endif /* ifdef DEBUG_STACK */