1 static const char CVSID
[] = "$Id: interpret.c,v 1.39 2004/05/12 09:21:40 edg Exp $";
2 /*******************************************************************************
4 * interpret.c -- Nirvana Editor macro interpreter *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
33 #include "interpret.h"
48 #include "../util/VMSparam.h"
51 #include <sys/param.h>
55 #include <X11/Intrinsic.h>
64 #define PROGRAM_SIZE 4096 /* Maximum program size */
65 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
66 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
67 allowed per program */
68 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
69 allowed to execute before preempting and
70 returning to allow other things to run */
72 /* Temporary markers placed in a branch address location to designate
73 which loop address (break or continue) the location needs */
74 #define NEEDS_BREAK (Inst)1
75 #define NEEDS_CONTINUE (Inst)2
77 #define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */
79 enum opStatusCodes
{STAT_OK
=2, STAT_DONE
, STAT_ERROR
, STAT_PREEMPT
};
81 static void addLoopAddr(Inst
*addr
);
82 static void saveContext(RestartData
*context
);
83 static void restoreContext(RestartData
*context
);
84 static int returnNoVal(void);
85 static int returnVal(void);
86 static int returnValOrNone(int valOnStack
);
87 static int pushSymVal(void);
88 static int pushArgVal(void);
89 static int pushArgCount(void);
90 static int pushArgArray(void);
91 static int pushArraySymVal(void);
92 static int dupStack(void);
94 static int subtract(void);
95 static int multiply(void);
96 static int divide(void);
97 static int modulo(void);
98 static int negate(void);
99 static int increment(void);
100 static int decrement(void);
107 static int bitAnd(void);
108 static int bitOr(void);
109 static int and(void);
111 static int not(void);
112 static int power(void);
113 static int concat(void);
114 static int assign(void);
115 static int callSubroutine(void);
116 static int fetchRetVal(void);
117 static int branch(void);
118 static int branchTrue(void);
119 static int branchFalse(void);
120 static int branchNever(void);
121 static int arrayRef(void);
122 static int arrayAssign(void);
123 static int arrayRefAndAssignSetup(void);
124 static int beginArrayIter(void);
125 static int arrayIter(void);
126 static int inArray(void);
127 static int deleteArrayElement(void);
128 static void freeSymbolTable(Symbol
*symTab
);
129 static int errCheck(const char *s
);
130 static int execError(const char *s1
, const char *s2
);
131 static rbTreeNode
*arrayEmptyAllocator(void);
132 static rbTreeNode
*arrayAllocateNode(rbTreeNode
*src
);
133 static int arrayEntryCopyToNode(rbTreeNode
*dst
, rbTreeNode
*src
);
134 static int arrayEntryCompare(rbTreeNode
*left
, rbTreeNode
*right
);
135 static void arrayDisposeNode(rbTreeNode
*src
);
136 static SparseArrayEntry
*allocateSparseArrayEntry(void);
138 /*#define DEBUG_ASSEMBLY*/
139 /*#define DEBUG_STACK*/
141 #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK)
142 #define DEBUG_DISASSEMBLER
143 static void disasm(Inst
*inst
, int nInstr
);
144 #endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */
146 #ifdef DEBUG_ASSEMBLY /* for disassembly */
147 #define DISASM(i, n) disasm(i, n)
148 #else /* #ifndef DEBUG_ASSEMBLY */
150 #endif /* #ifndef DEBUG_ASSEMBLY */
152 #ifdef DEBUG_STACK /* for run-time instruction and stack trace */
153 static void stackdump(int n
, int extra
);
154 #define STACKDUMP(n, x) stackdump(n, x)
155 #define DISASM_RT(i, n) disasm(i, n)
156 #else /* #ifndef DEBUG_STACK */
157 #define STACKDUMP(n, x)
158 #define DISASM_RT(i, n)
159 #endif /* #ifndef DEBUG_STACK */
161 /* Global symbols and function definitions */
162 static Symbol
*GlobalSymList
= NULL
;
164 /* List of all memory allocated for strings */
165 static char *AllocatedStrings
= NULL
;
168 SparseArrayEntry data
; /* LEAVE this as top entry */
169 int inUse
; /* we use pointers to the data to refer to the entire struct */
170 struct SparseArrayEntryWrapper
*next
;
171 } SparseArrayEntryWrapper
;
173 static SparseArrayEntryWrapper
*AllocatedSparseArrayEntries
= NULL
;
175 /* Message strings used in macros (so they don't get repeated every time
176 the macros are used */
177 static const char *StackOverflowMsg
= "macro stack overflow";
178 static const char *StackUnderflowMsg
= "macro stack underflow";
179 static const char *StringToNumberMsg
= "string could not be converted to number";
181 /* Temporary global data for use while accumulating programs */
182 static Symbol
*LocalSymList
= NULL
; /* symbols local to the program */
183 static Inst Prog
[PROGRAM_SIZE
]; /* the program */
184 static Inst
*ProgP
; /* next free spot for code gen. */
185 static Inst
*LoopStack
[LOOP_STACK_SIZE
]; /* addresses of break, cont stmts */
186 static Inst
**LoopStackPtr
= LoopStack
; /* to fill at the end of a loop */
188 /* Global data for the interpreter */
189 static DataValue
*Stack
; /* the stack */
190 static DataValue
*StackP
; /* next free spot on stack */
191 static DataValue
*FrameP
; /* frame pointer (start of local variables
192 for the current subroutine invocation) */
193 static Inst
*PC
; /* program counter during execution */
194 static char *ErrMsg
; /* global for returning error messages
195 from executing functions */
197 *InitiatingWindow
= NULL
; /* window from which macro was run */
198 static WindowInfo
*FocusWindow
; /* window on which macro commands operate */
199 static int PreemptRequest
; /* passes preemption requests from called
200 routines back up to the interpreter */
202 /* Array for mapping operations to functions for performing the operations
203 Must correspond to the enum called "operations" in interpret.h */
204 static int (*OpFns
[N_OPS
])() = {returnNoVal
, returnVal
, pushSymVal
, dupStack
,
205 add
, subtract
, multiply
, divide
, modulo
, negate
, increment
, decrement
,
206 gt
, lt
, ge
, le
, eq
, ne
, bitAnd
, bitOr
, and, or, not, power
, concat
,
207 assign
, callSubroutine
, fetchRetVal
, branch
, branchTrue
, branchFalse
,
208 branchNever
, arrayRef
, arrayAssign
, beginArrayIter
, arrayIter
, inArray
,
209 deleteArrayElement
, pushArraySymVal
,
210 arrayRefAndAssignSetup
, pushArgVal
, pushArgCount
, pushArgArray
};
212 /* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
213 #define FP_ARG_ARRAY_CACHE_INDEX (-1)
214 #define FP_ARG_COUNT_INDEX (-2)
215 #define FP_OLD_FP_INDEX (-3)
216 #define FP_RET_PC_INDEX (-4)
217 #define FP_TO_ARGS_DIST (4) /* should be 0 - (above index) */
218 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
219 #define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_CACHE_INDEX))
220 #define FP_GET_ARG_COUNT(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_COUNT_INDEX).val.n)
221 #define FP_GET_OLD_FP(xFrameP) ((FP_GET_ITEM(xFrameP, FP_OLD_FP_INDEX)).val.dataval)
222 #define FP_GET_RET_PC(xFrameP) ((FP_GET_ITEM(xFrameP, FP_RET_PC_INDEX)).val.inst)
223 #define FP_ARG_START_INDEX(xFrameP) (-(FP_GET_ARG_COUNT(xFrameP) + FP_TO_ARGS_DIST))
224 #define FP_GET_ARG_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN + FP_ARG_START_INDEX(xFrameP)))
225 #define FP_GET_SYM_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN))
226 #define FP_GET_SYM_VAL(xFrameP,xSym) (FP_GET_SYM_N(xFrameP, xSym->value.val.n))
229 ** Initialize macro language global variables. Must be called before
230 ** any macros are even parsed, because the parser uses action routine
231 ** symbols to comprehend hyphenated names.
233 void InitMacroGlobals(void)
235 XtActionsRec
*actions
;
237 static char argName
[3] = "$x";
238 static DataValue dv
= {NO_TAG
, {0}};
240 /* Add action routines from NEdit menus and text widget */
241 actions
= GetMenuActions(&nActions
);
242 for (i
=0; i
<nActions
; i
++) {
243 dv
.val
.xtproc
= actions
[i
].proc
;
244 InstallSymbol(actions
[i
].string
, ACTION_ROUTINE_SYM
, dv
);
246 actions
= TextGetActions(&nActions
);
247 for (i
=0; i
<nActions
; i
++) {
248 dv
.val
.xtproc
= actions
[i
].proc
;
249 InstallSymbol(actions
[i
].string
, ACTION_ROUTINE_SYM
, dv
);
252 /* Add subroutine argument symbols ($1, $2, ..., $9) */
253 for (i
=0; i
<9; i
++) {
254 argName
[1] = '1' + i
;
256 InstallSymbol(argName
, ARG_SYM
, dv
);
259 /* Add special symbol $n_args */
260 dv
.val
.n
= N_ARGS_ARG_SYM
;
261 InstallSymbol("$n_args", ARG_SYM
, dv
);
265 ** To build a program for the interpreter, call BeginCreatingProgram, to
266 ** begin accumulating the program, followed by calls to AddOp, AddSym,
267 ** and InstallSymbol to add symbols and operations. When the new program
268 ** is finished, collect the results with FinishCreatingProgram. This returns
269 ** a self contained program that can be run with ExecuteMacro.
273 ** Start collecting instructions for a program. Clears the program
274 ** and the symbol table.
276 void BeginCreatingProgram(void)
280 LoopStackPtr
= LoopStack
;
284 ** Finish up the program under construction, and return it (code and
285 ** symbol table) as a package that ExecuteMacro can execute. This
286 ** program must be freed with FreeProgram.
288 Program
*FinishCreatingProgram(void)
291 int progLen
, fpOffset
= 0;
294 newProg
= (Program
*)XtMalloc(sizeof(Program
));
295 progLen
= ((char *)ProgP
) - ((char *)Prog
);
296 newProg
->code
= (Inst
*)XtMalloc(progLen
);
297 memcpy(newProg
->code
, Prog
, progLen
);
298 newProg
->localSymList
= LocalSymList
;
301 /* Local variables' values are stored on the stack. Here we assign
302 frame pointer offsets to them. */
303 for (s
= newProg
->localSymList
; s
!= NULL
; s
= s
->next
)
304 s
->value
.val
.n
= fpOffset
++;
306 DISASM(newProg
->code
, ProgP
- Prog
);
311 void FreeProgram(Program
*prog
)
313 freeSymbolTable(prog
->localSymList
);
314 XtFree((char *)prog
->code
);
315 XtFree((char *)prog
);
319 ** Add an operator (instruction) to the end of the current program
321 int AddOp(int op
, char **msg
)
323 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
324 *msg
= "macro too large";
327 *ProgP
++ = OpFns
[op
];
332 ** Add a symbol operand to the current program
334 int AddSym(Symbol
*sym
, char **msg
)
336 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
337 *msg
= "macro too large";
340 *ProgP
++ = (Inst
)sym
;
345 ** Add an immediate value operand to the current program
347 int AddImmediate(void *value
, char **msg
)
349 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
350 *msg
= "macro too large";
353 *ProgP
++ = (Inst
)value
;
358 ** Add a branch offset operand to the current program
360 int AddBranchOffset(Inst
*to
, char **msg
)
362 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
363 *msg
= "macro too large";
366 *ProgP
= (Inst
)(to
- ProgP
);
373 ** Return the address at which the next instruction will be stored
381 ** Swap the positions of two contiguous blocks of code. The first block
382 ** running between locations start and boundary, and the second between
385 void SwapCode(Inst
*start
, Inst
*boundary
, Inst
*end
)
387 #define reverseCode(L, H) \
388 do { register Inst t, *l = L, *h = H - 1; \
389 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
390 /* double-reverse method: reverse elements of both parts then whole lot */
391 /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
392 reverseCode(start
, boundary
); /* 1 */
393 reverseCode(boundary
, end
); /* 2 */
394 reverseCode(start
, end
); /* 3 */
398 ** Maintain a stack to save addresses of branch operations for break and
399 ** continue statements, so they can be filled in once the information
400 ** on where to branch is known.
402 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or
403 ** AddContinueAddr to register the address at which to store the branch
404 ** address for a break or continue statement, and FillLoopAddrs to fill
405 ** in all the addresses and return to the level of the enclosing loop.
407 void StartLoopAddrList(void)
412 int AddBreakAddr(Inst
*addr
)
414 if (LoopStackPtr
== LoopStack
) return 1;
420 int AddContinueAddr(Inst
*addr
)
422 if (LoopStackPtr
== LoopStack
) return 1;
424 *addr
= NEEDS_CONTINUE
;
428 static void addLoopAddr(Inst
*addr
)
430 if (LoopStackPtr
> &LoopStack
[LOOP_STACK_SIZE
-1]) {
431 fprintf(stderr
, "NEdit: loop stack overflow in macro parser");
434 *LoopStackPtr
++ = addr
;
437 void FillLoopAddrs(Inst
*breakAddr
, Inst
*continueAddr
)
441 if (LoopStackPtr
< LoopStack
) {
442 fprintf(stderr
, "NEdit: internal error (lsu) in macro parser\n");
445 if (*LoopStackPtr
== NULL
)
447 if (**LoopStackPtr
== NEEDS_BREAK
)
448 **(Inst
***)LoopStackPtr
= (Inst
*)(breakAddr
- *LoopStackPtr
);
449 else if (**LoopStackPtr
== NEEDS_CONTINUE
)
450 **(Inst
***)LoopStackPtr
= (Inst
*)(continueAddr
- *LoopStackPtr
);
452 fprintf(stderr
, "NEdit: internal error (uat) in macro parser\n");
457 ** Execute a compiled macro, "prog", using the arguments in the array
458 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.
459 ** if MACRO_DONE is returned, the macro completed, and the returned value
460 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the
461 ** macro exceeded its alotted time-slice and scheduled...
463 int ExecuteMacro(WindowInfo
*window
, Program
*prog
, int nArgs
, DataValue
*args
,
464 DataValue
*result
, RestartData
**continuation
, char **msg
)
466 RestartData
*context
;
467 static DataValue noValue
= {NO_TAG
, {0}};
471 /* Create an execution context (a stack, a stack pointer, a frame pointer,
472 and a program counter) which will retain the program state across
473 preemption and resumption of execution */
474 context
= (RestartData
*)XtMalloc(sizeof(RestartData
));
475 context
->stack
= (DataValue
*)XtMalloc(sizeof(DataValue
) * STACK_SIZE
);
476 *continuation
= context
;
477 context
->stackP
= context
->stack
;
478 context
->pc
= prog
->code
;
479 context
->runWindow
= window
;
480 context
->focusWindow
= window
;
482 /* Push arguments and call information onto the stack */
483 for (i
=0; i
<nArgs
; i
++)
484 *(context
->stackP
++) = args
[i
];
486 context
->stackP
->val
.subr
= NULL
; /* return PC */
487 context
->stackP
->tag
= NO_TAG
;
490 *(context
->stackP
++) = noValue
; /* old FrameP */
492 context
->stackP
->tag
= NO_TAG
; /* nArgs */
493 context
->stackP
->val
.n
= nArgs
;
496 *(context
->stackP
++) = noValue
; /* cached arg array */
498 context
->frameP
= context
->stackP
;
500 /* Initialize and make room on the stack for local variables */
501 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
502 FP_GET_SYM_VAL(context
->frameP
, s
) = noValue
;
506 /* Begin execution, return on error or preemption */
507 return ContinueMacro(context
, result
, msg
);
511 ** Continue the execution of a suspended macro whose state is described in
514 int ContinueMacro(RestartData
*continuation
, DataValue
*result
, char **msg
)
516 register int status
, instCount
= 0;
518 RestartData oldContext
;
520 /* To allow macros to be invoked arbitrarily (such as those automatically
521 triggered within smart-indent) within executing macros, this call is
523 saveContext(&oldContext
);
526 ** Execution Loop: Call the succesive routine addresses in the program
527 ** until one returns something other than STAT_OK, then take action
529 restoreContext(continuation
);
533 /* Execute an instruction */
537 /* If error return was not STAT_OK, return to caller */
538 if (status
!= STAT_OK
) {
539 if (status
== STAT_PREEMPT
) {
540 saveContext(continuation
);
541 restoreContext(&oldContext
);
542 return MACRO_PREEMPT
;
543 } else if (status
== STAT_ERROR
) {
545 FreeRestartData(continuation
);
546 restoreContext(&oldContext
);
548 } else if (status
== STAT_DONE
) {
551 FreeRestartData(continuation
);
552 restoreContext(&oldContext
);
557 /* Count instructions executed. If the instruction limit is hit,
558 preempt, store re-start information in continuation and give
559 X, other macros, and other shell scripts a chance to execute */
561 if (instCount
>= INSTRUCTION_LIMIT
) {
562 saveContext(continuation
);
563 restoreContext(&oldContext
);
564 return MACRO_TIME_LIMIT
;
570 ** If a macro is already executing, and requests that another macro be run,
571 ** this can be called instead of ExecuteMacro to run it in the same context
572 ** as if it were a subroutine. This saves the caller from maintaining
573 ** separate contexts, and serializes processing of the two macros without
576 void RunMacroAsSubrCall(Program
*prog
)
579 static DataValue noValue
= {NO_TAG
, {0}};
581 /* See subroutine "callSubroutine" for a description of the stack frame
582 for a subroutine call */
583 StackP
->tag
= NO_TAG
;
584 StackP
->val
.inst
= PC
; /* return PC */
587 StackP
->tag
= NO_TAG
;
588 StackP
->val
.dataval
= FrameP
; /* old FrameP */
591 StackP
->tag
= NO_TAG
; /* nArgs */
595 *(StackP
++) = noValue
; /* cached arg array */
599 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
600 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
605 void FreeRestartData(RestartData
*context
)
607 XtFree((char *)context
->stack
);
608 XtFree((char *)context
);
612 ** Cause a macro in progress to be preempted (called by commands which take
613 ** a long time, or want to return to the event loop. Call ResumeMacroExecution
616 void PreemptMacro(void)
618 PreemptRequest
= True
;
622 ** Reset the return value for a subroutine which caused preemption (this is
623 ** how to return a value from a routine which preempts instead of returning
624 ** a value directly).
626 void ModifyReturnedValue(RestartData
*context
, DataValue dv
)
628 if (*(context
->pc
-1) == fetchRetVal
)
629 *(context
->stackP
-1) = dv
;
633 ** Called within a routine invoked from a macro, returns the window in
634 ** which the macro is executing (where the banner is, not where it is focused)
636 WindowInfo
*MacroRunWindow(void)
638 return InitiatingWindow
;
642 ** Called within a routine invoked from a macro, returns the window to which
643 ** the currently executing macro is focused (the window which macro commands
644 ** modify, not the window from which the macro is being run)
646 WindowInfo
*MacroFocusWindow(void)
652 ** Set the window to which macro subroutines and actions which operate on an
653 ** implied window are directed.
655 void SetMacroFocusWindow(WindowInfo
*window
)
657 FocusWindow
= window
;
661 ** install an array iteration symbol
662 ** it is tagged as an integer but holds an array node pointer
664 #define ARRAY_ITER_SYM_PREFIX "aryiter "
665 Symbol
*InstallIteratorSymbol()
667 char symbolName
[sizeof(ARRAY_ITER_SYM_PREFIX
) + TYPE_INT_STR_SIZE(int)];
669 static int interatorNameIndex
= 0;
671 sprintf(symbolName
, ARRAY_ITER_SYM_PREFIX
"#%d", interatorNameIndex
);
672 ++interatorNameIndex
;
674 value
.val
.arrayPtr
= NULL
;
675 return(InstallSymbol(symbolName
, LOCAL_SYM
, value
));
679 ** Lookup a constant string by its value. This allows reuse of string
680 ** constants and fixing a leak in the interpreter.
682 Symbol
*LookupStringConstSymbol(const char *value
)
686 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
) {
687 if (s
->type
== CONST_SYM
&&
688 s
->value
.tag
== STRING_TAG
&&
689 !strcmp(s
->value
.val
.str
.rep
, value
)) {
697 ** install string str in the global symbol table with a string name
699 Symbol
*InstallStringConstSymbol(const char *str
)
701 static int stringConstIndex
= 0;
704 Symbol
*sym
= LookupStringConstSymbol(str
);
709 sprintf(stringName
, "string #%d", stringConstIndex
++);
710 value
.tag
= STRING_TAG
;
711 AllocNStringCpy(&value
.val
.str
, str
);
712 return(InstallSymbol(stringName
, CONST_SYM
, value
));
716 ** find a symbol in the symbol table
718 Symbol
*LookupSymbol(const char *name
)
722 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
)
723 if (strcmp(s
->name
, name
) == 0)
725 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
726 if (strcmp(s
->name
, name
) == 0)
732 ** install symbol name in symbol table
734 Symbol
*InstallSymbol(const char *name
, enum symTypes type
, DataValue value
)
738 s
= (Symbol
*)malloc(sizeof(Symbol
));
739 s
->name
= (char *)malloc(strlen(name
)+1); /* +1 for '\0' */
740 strcpy(s
->name
, name
);
743 if (type
== LOCAL_SYM
) {
744 s
->next
= LocalSymList
;
747 s
->next
= GlobalSymList
;
754 ** Promote a symbol from local to global, removing it from the local symbol
757 Symbol
*PromoteToGlobal(Symbol
*sym
)
760 static DataValue noValue
= {NO_TAG
, {0}};
762 if (sym
->type
!= LOCAL_SYM
)
765 /* Remove sym from the local symbol list */
766 if (sym
== LocalSymList
)
767 LocalSymList
= sym
->next
;
769 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
) {
770 if (s
->next
== sym
) {
777 s
= LookupSymbol(sym
->name
);
780 return InstallSymbol(sym
->name
, GLOBAL_SYM
, noValue
);
784 ** Allocate memory for a string, and keep track of it, such that it
785 ** can be recovered later using GarbageCollectStrings. (A linked list
786 ** of pointers is maintained by threading through the memory behind
787 ** the returned pointers). Length does not include the terminating null
788 ** character, so to allocate space for a string of strlen == n, you must
789 ** use AllocString(n+1).
792 /*#define TRACK_GARBAGE_LEAKS*/
793 #ifdef TRACK_GARBAGE_LEAKS
794 static int numAllocatedStrings
= 0;
795 static int numAllocatedSparseArrayElements
= 0;
798 /* Allocate a new string buffer of length chars */
799 char *AllocString(int length
)
803 mem
= XtMalloc(length
+ sizeof(char *) + 1);
804 *((char **)mem
) = AllocatedStrings
;
805 AllocatedStrings
= mem
;
806 #ifdef TRACK_GARBAGE_LEAKS
807 ++numAllocatedStrings
;
809 return mem
+ sizeof(char *) + 1;
813 * Allocate a new NString buffer of length chars (terminating \0 included),
814 * The buffer length is initialized to length-1 and the terminating \0 is
817 int AllocNString(NString
*string
, int length
)
821 mem
= XtMalloc(length
+ sizeof(char *) + 1);
828 *((char **)mem
) = AllocatedStrings
;
829 AllocatedStrings
= mem
;
830 #ifdef TRACK_GARBAGE_LEAKS
831 ++numAllocatedStrings
;
833 string
->rep
= mem
+ sizeof(char *) + 1;
834 string
->rep
[length
-1] = '\0'; /* forced \0 */
835 string
->len
= length
-1;
839 /* Allocate a new string buffer of length chars, and copy in the string s */
840 char *AllocStringNCpy(const char *s
, int length
)
842 char *p
= AllocString(length
+ 1); /* add extra char for forced \0 */
847 p
[length
] = '\0'; /* forced \0 */
848 return strncpy(p
, s
, length
);
852 * Allocate a new NString buffer of length chars (terminating \0 NOT included),
853 * and copy at most length characters of the given string.
854 * The buffer length is properly set and the buffer is guaranteed to be
857 int AllocNStringNCpy(NString
*string
, const char *s
, int length
)
859 if (!AllocNString(string
, length
+ 1)) /* add extra char for forced \0 */
863 strncpy(string
->rep
, s
, length
);
864 string
->len
= strlen(string
->rep
); /* re-calculate! */
868 /* Allocate a new copy of string s */
869 char *AllocStringCpy(const char *s
)
871 return AllocStringNCpy(s
, s
? strlen(s
) : 0);
875 * Allocate a new NString buffer, containing a copy of the given string.
876 * The length is set to the length of the string and resulting string is
877 * guaranteed to be \0-terminated.
879 int AllocNStringCpy(NString
*string
, const char *s
)
881 size_t length
= s
? strlen(s
) : 0;
882 if (!AllocNString(string
, length
+ 1))
885 strncpy(string
->rep
, s
, length
);
889 static SparseArrayEntry
*allocateSparseArrayEntry(void)
891 SparseArrayEntryWrapper
*mem
;
893 mem
= (SparseArrayEntryWrapper
*)XtMalloc(sizeof(SparseArrayEntryWrapper
));
894 mem
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
895 AllocatedSparseArrayEntries
= mem
;
896 #ifdef TRACK_GARBAGE_LEAKS
897 ++numAllocatedSparseArrayElements
;
899 return(&(mem
->data
));
902 static void MarkArrayContentsAsUsed(SparseArrayEntry
*arrayPtr
)
904 SparseArrayEntry
*globalSEUse
;
907 ((SparseArrayEntryWrapper
*)arrayPtr
)->inUse
= 1;
908 for (globalSEUse
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)arrayPtr
);
910 globalSEUse
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)globalSEUse
)) {
912 ((SparseArrayEntryWrapper
*)globalSEUse
)->inUse
= 1;
913 /* test first because it may be read-only static string */
914 if (!(*(globalSEUse
->key
- 1))) {
915 *(globalSEUse
->key
- 1) = 1;
917 if (globalSEUse
->value
.tag
== STRING_TAG
) {
918 /* test first because it may be read-only static string */
919 if (!(*(globalSEUse
->value
.val
.str
.rep
- 1))) {
920 *(globalSEUse
->value
.val
.str
.rep
- 1) = 1;
923 else if (globalSEUse
->value
.tag
== ARRAY_TAG
) {
924 MarkArrayContentsAsUsed((SparseArrayEntry
*)globalSEUse
->value
.val
.arrayPtr
);
931 ** Collect strings that are no longer referenced from the global symbol
932 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must
933 ** only be run after all macro activity has ceased.
936 void GarbageCollectStrings(void)
938 SparseArrayEntryWrapper
*nextAP
, *thisAP
;
942 /* mark all strings as unreferenced */
943 for (p
= AllocatedStrings
; p
!= NULL
; p
= *((char **)p
)) {
944 *(p
+ sizeof(char *)) = 0;
947 for (thisAP
= AllocatedSparseArrayEntries
;
948 thisAP
!= NULL
; thisAP
= (SparseArrayEntryWrapper
*)thisAP
->next
) {
952 /* Sweep the global symbol list, marking which strings are still
954 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
) {
955 if (s
->value
.tag
== STRING_TAG
) {
956 /* test first because it may be read-only static string */
957 if (!(*(s
->value
.val
.str
.rep
- 1))) {
958 *(s
->value
.val
.str
.rep
- 1) = 1;
961 else if (s
->value
.tag
== ARRAY_TAG
) {
962 MarkArrayContentsAsUsed((SparseArrayEntry
*)s
->value
.val
.arrayPtr
);
966 /* Collect all of the strings which remain unreferenced */
967 next
= AllocatedStrings
;
968 AllocatedStrings
= NULL
;
969 while (next
!= NULL
) {
971 next
= *((char **)p
);
972 if (*(p
+ sizeof(char *)) != 0) {
973 *((char **)p
) = AllocatedStrings
;
974 AllocatedStrings
= p
;
977 #ifdef TRACK_GARBAGE_LEAKS
978 --numAllocatedStrings
;
984 nextAP
= AllocatedSparseArrayEntries
;
985 AllocatedSparseArrayEntries
= NULL
;
986 while (nextAP
!= NULL
) {
988 nextAP
= (SparseArrayEntryWrapper
*)nextAP
->next
;
989 if (thisAP
->inUse
!= 0) {
990 thisAP
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
991 AllocatedSparseArrayEntries
= thisAP
;
994 #ifdef TRACK_GARBAGE_LEAKS
995 --numAllocatedSparseArrayElements
;
997 XtFree((void *)thisAP
);
1001 #ifdef TRACK_GARBAGE_LEAKS
1002 printf("str count = %d\nary count = %d\n", numAllocatedStrings
, numAllocatedSparseArrayElements
);
1007 ** Save and restore execution context to data structure "context"
1009 static void saveContext(RestartData
*context
)
1011 context
->stack
= Stack
;
1012 context
->stackP
= StackP
;
1013 context
->frameP
= FrameP
;
1015 context
->runWindow
= InitiatingWindow
;
1016 context
->focusWindow
= FocusWindow
;
1019 static void restoreContext(RestartData
*context
)
1021 Stack
= context
->stack
;
1022 StackP
= context
->stackP
;
1023 FrameP
= context
->frameP
;
1025 InitiatingWindow
= context
->runWindow
;
1026 FocusWindow
= context
->focusWindow
;
1029 static void freeSymbolTable(Symbol
*symTab
)
1033 while(symTab
!= NULL
) {
1041 #define POP(dataVal) \
1042 if (StackP == Stack) \
1043 return execError(StackUnderflowMsg, ""); \
1044 dataVal = *--StackP;
1046 #define PUSH(dataVal) \
1047 if (StackP >= &Stack[STACK_SIZE]) \
1048 return execError(StackOverflowMsg, ""); \
1049 *StackP++ = dataVal;
1051 #define PEEK(dataVal, peekIndex) \
1052 dataVal = *(StackP - peekIndex - 1);
1054 #define POP_INT(number) \
1055 if (StackP == Stack) \
1056 return execError(StackUnderflowMsg, ""); \
1058 if (StackP->tag == STRING_TAG) { \
1059 if (!StringToNum(StackP->val.str.rep, &number)) \
1060 return execError(StringToNumberMsg, ""); \
1061 } else if (StackP->tag == INT_TAG) \
1062 number = StackP->val.n; \
1064 return(execError("can't convert array to integer", NULL));
1066 #define POP_STRING(string) \
1067 if (StackP == Stack) \
1068 return execError(StackUnderflowMsg, ""); \
1070 if (StackP->tag == INT_TAG) { \
1071 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1072 sprintf(string, "%d", StackP->val.n); \
1073 } else if (StackP->tag == STRING_TAG) \
1074 string = StackP->val.str.rep; \
1076 return(execError("can't convert array to string", NULL));
1078 #define PEEK_STRING(string, peekIndex) \
1079 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1080 string = AllocString(TYPE_INT_STR_SIZE(int)); \
1081 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \
1083 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1084 string = (StackP - peekIndex - 1)->val.str; \
1087 return(execError("can't convert array to string", NULL)); \
1090 #define PEEK_INT(number, peekIndex) \
1091 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
1092 if (!StringToNum((StackP - peekIndex - 1)->val.str, &number)) { \
1093 return execError(StringToNumberMsg, ""); \
1095 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
1096 number = (StackP - peekIndex - 1)->val.n; \
1099 return(execError("can't convert array to string", NULL)); \
1102 #define PUSH_INT(number) \
1103 if (StackP >= &Stack[STACK_SIZE]) \
1104 return execError(StackOverflowMsg, ""); \
1105 StackP->tag = INT_TAG; \
1106 StackP->val.n = number; \
1109 #define PUSH_STRING(string, length) \
1110 if (StackP >= &Stack[STACK_SIZE]) \
1111 return execError(StackOverflowMsg, ""); \
1112 StackP->tag = STRING_TAG; \
1113 StackP->val.str.rep = string; \
1114 StackP->val.str.len = length; \
1117 #define BINARY_NUMERIC_OPERATION(operator) \
1119 DISASM_RT(PC-1, 1); \
1123 PUSH_INT(n1 operator n2) \
1126 #define UNARY_NUMERIC_OPERATION(operator) \
1128 DISASM_RT(PC-1, 1); \
1131 PUSH_INT(operator n) \
1135 ** copy a symbol's value onto the stack
1136 ** Before: Prog-> [Sym], next, ...
1137 ** Stack-> next, ...
1138 ** After: Prog-> Sym, [next], ...
1139 ** Stack-> [SymValue], next, ...
1141 static int pushSymVal(void)
1149 s
= (Symbol
*)*PC
++;
1150 if (s
->type
== LOCAL_SYM
) {
1151 *StackP
= FP_GET_SYM_VAL(FrameP
, s
);
1152 } else if (s
->type
== GLOBAL_SYM
|| s
->type
== CONST_SYM
) {
1154 } else if (s
->type
== ARG_SYM
) {
1155 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1156 argNum
= s
->value
.val
.n
;
1157 if (argNum
>= nArgs
) {
1158 return execError("referenced undefined argument: %s", s
->name
);
1160 if (argNum
== N_ARGS_ARG_SYM
) {
1161 StackP
->tag
= INT_TAG
;
1162 StackP
->val
.n
= nArgs
;
1165 *StackP
= FP_GET_ARG_N(FrameP
, argNum
);
1167 } else if (s
->type
== PROC_VALUE_SYM
) {
1170 if (!(s
->value
.val
.subr
)(FocusWindow
, NULL
, 0,
1171 &result
, &errMsg
)) {
1172 return execError(errMsg
, s
->name
);
1176 return execError("reading non-variable: %s", s
->name
);
1177 if (StackP
->tag
== NO_TAG
) {
1178 return execError("variable not set: %s", s
->name
);
1181 if (StackP
>= &Stack
[STACK_SIZE
]) {
1182 return execError(StackOverflowMsg
, "");
1187 static int pushArgVal(void)
1196 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1197 if (argNum
>= nArgs
|| argNum
< 0) {
1198 char argStr
[TYPE_INT_STR_SIZE(argNum
)];
1199 sprintf(argStr
, "%d", argNum
+ 1);
1200 return execError("referenced undefined argument: $args[%s]", argStr
);
1202 PUSH(FP_GET_ARG_N(FrameP
, argNum
));
1206 static int pushArgCount(void)
1211 PUSH_INT(FP_GET_ARG_COUNT(FrameP
));
1215 static int pushArgArray(void)
1218 DataValue argVal
, *resultArray
;
1223 nArgs
= FP_GET_ARG_COUNT(FrameP
);
1224 resultArray
= &FP_GET_ARG_ARRAY_CACHE(FrameP
);
1225 if (resultArray
->tag
!= ARRAY_TAG
) {
1226 resultArray
->tag
= ARRAY_TAG
;
1227 resultArray
->val
.arrayPtr
= ArrayNew();
1229 for (argNum
= 0; argNum
< nArgs
; ++argNum
) {
1230 char intStr
[TYPE_INT_STR_SIZE(argNum
)];
1232 sprintf(intStr
, "%d", argNum
+ 1);
1233 argVal
= FP_GET_ARG_N(FrameP
, argNum
);
1234 if (!ArrayInsert(resultArray
, AllocStringCpy(intStr
), &argVal
)) {
1235 return(execError("array insertion failure", NULL
));
1244 ** Push an array (by reference) onto the stack
1245 ** Before: Prog-> [ArraySym], makeEmpty, next, ...
1246 ** Stack-> next, ...
1247 ** After: Prog-> ArraySym, makeEmpty, [next], ...
1248 ** Stack-> [elemValue], next, ...
1249 ** makeEmpty is either true (1) or false (0): if true, and the element is not
1250 ** present in the array, create it.
1252 static int pushArraySymVal(void)
1261 sym
= (Symbol
*)*PC
;
1263 initEmpty
= (int)*PC
;
1266 if (sym
->type
== LOCAL_SYM
) {
1267 dataPtr
= &FP_GET_SYM_VAL(FrameP
, sym
);
1269 else if (sym
->type
== GLOBAL_SYM
) {
1270 dataPtr
= &sym
->value
;
1273 return execError("assigning to non-lvalue array or non-array: %s", sym
->name
);
1276 if (initEmpty
&& dataPtr
->tag
== NO_TAG
) {
1277 dataPtr
->tag
= ARRAY_TAG
;
1278 dataPtr
->val
.arrayPtr
= ArrayNew();
1281 if (dataPtr
->tag
== NO_TAG
) {
1282 return execError("variable not set: %s", sym
->name
);
1288 if (StackP
>= &Stack
[STACK_SIZE
]) {
1289 return execError(StackOverflowMsg
, "");
1295 ** assign top value to next symbol
1297 ** Before: Prog-> [symbol], next, ...
1298 ** Stack-> [value], next, ...
1299 ** After: Prog-> symbol, [next], ...
1300 ** Stack-> next, ...
1302 static int assign(void)
1310 sym
= (Symbol
*)(*PC
++);
1311 if (sym
->type
!= GLOBAL_SYM
&& sym
->type
!= LOCAL_SYM
) {
1312 if (sym
->type
== ARG_SYM
) {
1313 return execError("assignment to function argument: %s", sym
->name
);
1315 else if (sym
->type
== PROC_VALUE_SYM
) {
1316 return execError("assignment to read-only variable: %s", sym
->name
);
1319 return execError("assignment to non-variable: %s", sym
->name
);
1322 if (StackP
== Stack
) {
1323 return execError(StackUnderflowMsg
, "");
1326 if (sym
->type
== LOCAL_SYM
) {
1327 dataPtr
= &FP_GET_SYM_VAL(FrameP
, sym
);
1330 dataPtr
= &sym
->value
;
1332 if (StackP
->tag
== ARRAY_TAG
) {
1333 ArrayCopy(dataPtr
, StackP
);
1342 ** copy the top value of the stack
1343 ** Before: Stack-> value, next, ...
1344 ** After: Stack-> value, value, next, ...
1346 static int dupStack(void)
1351 if (StackP
>= &Stack
[STACK_SIZE
]) {
1352 return execError(StackOverflowMsg
, "");
1354 *StackP
= *(StackP
- 1);
1360 ** if left and right arguments are arrays, then the result is a new array
1361 ** in which all the keys from both the right and left are copied
1362 ** the values from the right array are used in the result array when the
1363 ** keys are the same
1364 ** Before: Stack-> value2, value1, next, ...
1365 ** After: Stack-> resValue, next, ...
1367 static int add(void)
1369 DataValue leftVal
, rightVal
, resultArray
;
1376 if (rightVal
.tag
== ARRAY_TAG
) {
1378 if (leftVal
.tag
== ARRAY_TAG
) {
1379 SparseArrayEntry
*leftIter
, *rightIter
;
1380 resultArray
.tag
= ARRAY_TAG
;
1381 resultArray
.val
.arrayPtr
= ArrayNew();
1385 leftIter
= arrayIterateFirst(&leftVal
);
1386 rightIter
= arrayIterateFirst(&rightVal
);
1387 while (leftIter
|| rightIter
) {
1388 int insertResult
= 1;
1390 if (leftIter
&& rightIter
) {
1391 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1392 if (compareResult
< 0) {
1393 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1394 leftIter
= arrayIterateNext(leftIter
);
1396 else if (compareResult
> 0) {
1397 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1398 rightIter
= arrayIterateNext(rightIter
);
1401 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1402 leftIter
= arrayIterateNext(leftIter
);
1403 rightIter
= arrayIterateNext(rightIter
);
1406 else if (leftIter
) {
1407 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1408 leftIter
= arrayIterateNext(leftIter
);
1411 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1412 rightIter
= arrayIterateNext(rightIter
);
1414 if (!insertResult
) {
1415 return(execError("array insertion failure", NULL
));
1421 return(execError("can't mix math with arrays and non-arrays", NULL
));
1433 ** if left and right arguments are arrays, then the result is a new array
1434 ** in which only the keys which exist in the left array but not in the right
1436 ** Before: Stack-> value2, value1, next, ...
1437 ** After: Stack-> resValue, next, ...
1439 static int subtract(void)
1441 DataValue leftVal
, rightVal
, resultArray
;
1448 if (rightVal
.tag
== ARRAY_TAG
) {
1450 if (leftVal
.tag
== ARRAY_TAG
) {
1451 SparseArrayEntry
*leftIter
, *rightIter
;
1452 resultArray
.tag
= ARRAY_TAG
;
1453 resultArray
.val
.arrayPtr
= ArrayNew();
1457 leftIter
= arrayIterateFirst(&leftVal
);
1458 rightIter
= arrayIterateFirst(&rightVal
);
1460 int insertResult
= 1;
1462 if (leftIter
&& rightIter
) {
1463 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1464 if (compareResult
< 0) {
1465 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1466 leftIter
= arrayIterateNext(leftIter
);
1468 else if (compareResult
> 0) {
1469 rightIter
= arrayIterateNext(rightIter
);
1472 leftIter
= arrayIterateNext(leftIter
);
1473 rightIter
= arrayIterateNext(rightIter
);
1476 else if (leftIter
) {
1477 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1478 leftIter
= arrayIterateNext(leftIter
);
1480 if (!insertResult
) {
1481 return(execError("array insertion failure", NULL
));
1487 return(execError("can't mix math with arrays and non-arrays", NULL
));
1499 ** Other binary operators
1500 ** Before: Stack-> value2, value1, next, ...
1501 ** After: Stack-> resValue, next, ...
1503 ** Other unary operators
1504 ** Before: Stack-> value, next, ...
1505 ** After: Stack-> resValue, next, ...
1507 static int multiply(void)
1509 BINARY_NUMERIC_OPERATION(*)
1512 static int divide(void)
1522 return execError("division by zero", "");
1528 static int modulo(void)
1538 return execError("modulo by zero", "");
1544 static int negate(void)
1546 UNARY_NUMERIC_OPERATION(-)
1549 static int increment(void)
1551 UNARY_NUMERIC_OPERATION(++)
1554 static int decrement(void)
1556 UNARY_NUMERIC_OPERATION(--)
1561 BINARY_NUMERIC_OPERATION(>)
1566 BINARY_NUMERIC_OPERATION(<)
1571 BINARY_NUMERIC_OPERATION(>=)
1576 BINARY_NUMERIC_OPERATION(<=)
1580 ** verify that compares are between integers and/or strings only
1581 ** Before: Stack-> value1, value2, next, ...
1582 ** After: Stack-> resValue, next, ...
1583 ** where resValue is 1 for true, 0 for false
1594 if (v1
.tag
== INT_TAG
&& v2
.tag
== INT_TAG
) {
1595 v1
.val
.n
= v1
.val
.n
== v2
.val
.n
;
1597 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== STRING_TAG
) {
1598 v1
.val
.n
= !strcmp(v1
.val
.str
.rep
, v2
.val
.str
.rep
);
1600 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== INT_TAG
) {
1602 if (!StringToNum(v1
.val
.str
.rep
, &number
)) {
1606 v1
.val
.n
= number
== v2
.val
.n
;
1609 else if (v2
.tag
== STRING_TAG
&& v1
.tag
== INT_TAG
) {
1611 if (!StringToNum(v2
.val
.str
.rep
, &number
)) {
1615 v1
.val
.n
= number
== v1
.val
.n
;
1619 return(execError("incompatible types to compare", NULL
));
1626 /* negated eq() call */
1634 ** if left and right arguments are arrays, then the result is a new array
1635 ** in which only the keys which exist in both the right or left are copied
1636 ** the values from the right array are used in the result array
1637 ** Before: Stack-> value2, value1, next, ...
1638 ** After: Stack-> resValue, next, ...
1640 static int bitAnd(void)
1642 DataValue leftVal
, rightVal
, resultArray
;
1649 if (rightVal
.tag
== ARRAY_TAG
) {
1651 if (leftVal
.tag
== ARRAY_TAG
) {
1652 SparseArrayEntry
*leftIter
, *rightIter
;
1653 resultArray
.tag
= ARRAY_TAG
;
1654 resultArray
.val
.arrayPtr
= ArrayNew();
1658 leftIter
= arrayIterateFirst(&leftVal
);
1659 rightIter
= arrayIterateFirst(&rightVal
);
1660 while (leftIter
&& rightIter
) {
1661 int insertResult
= 1;
1662 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1664 if (compareResult
< 0) {
1665 leftIter
= arrayIterateNext(leftIter
);
1667 else if (compareResult
> 0) {
1668 rightIter
= arrayIterateNext(rightIter
);
1671 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1672 leftIter
= arrayIterateNext(leftIter
);
1673 rightIter
= arrayIterateNext(rightIter
);
1675 if (!insertResult
) {
1676 return(execError("array insertion failure", NULL
));
1682 return(execError("can't mix math with arrays and non-arrays", NULL
));
1694 ** if left and right arguments are arrays, then the result is a new array
1695 ** in which only the keys which exist in either the right or left but not both
1697 ** Before: Stack-> value2, value1, next, ...
1698 ** After: Stack-> resValue, next, ...
1700 static int bitOr(void)
1702 DataValue leftVal
, rightVal
, resultArray
;
1709 if (rightVal
.tag
== ARRAY_TAG
) {
1711 if (leftVal
.tag
== ARRAY_TAG
) {
1712 SparseArrayEntry
*leftIter
, *rightIter
;
1713 resultArray
.tag
= ARRAY_TAG
;
1714 resultArray
.val
.arrayPtr
= ArrayNew();
1718 leftIter
= arrayIterateFirst(&leftVal
);
1719 rightIter
= arrayIterateFirst(&rightVal
);
1720 while (leftIter
|| rightIter
) {
1721 int insertResult
= 1;
1723 if (leftIter
&& rightIter
) {
1724 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1725 if (compareResult
< 0) {
1726 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1727 leftIter
= arrayIterateNext(leftIter
);
1729 else if (compareResult
> 0) {
1730 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1731 rightIter
= arrayIterateNext(rightIter
);
1734 leftIter
= arrayIterateNext(leftIter
);
1735 rightIter
= arrayIterateNext(rightIter
);
1738 else if (leftIter
) {
1739 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1740 leftIter
= arrayIterateNext(leftIter
);
1743 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1744 rightIter
= arrayIterateNext(rightIter
);
1746 if (!insertResult
) {
1747 return(execError("array insertion failure", NULL
));
1753 return(execError("can't mix math with arrays and non-arrays", NULL
));
1764 static int and(void)
1766 BINARY_NUMERIC_OPERATION(&&)
1771 BINARY_NUMERIC_OPERATION(||)
1774 static int not(void)
1776 UNARY_NUMERIC_OPERATION(!)
1780 ** raise one number to the power of another
1781 ** Before: Stack-> raisedBy, number, next, ...
1782 ** After: Stack-> result, next, ...
1784 static int power(void)
1793 /* We need to round to deal with pow() giving results slightly above
1794 or below the real result since it deals with floating point numbers.
1795 Note: We're not really wanting rounded results, we merely
1796 want to deal with this simple issue. So, 2^-2 = .5, but we
1797 don't want to round this to 1. This is mainly intended to deal with
1798 4^2 = 15.999996 and 16.000001.
1800 if (n2
< 0 && n1
!= 1 && n1
!= -1) {
1802 /* since we're integer only, nearly all negative exponents result in 0 */
1806 /* allow error to occur */
1807 n3
= (int)pow((double)n1
, (double)n2
);
1811 if ((n1
< 0) && (n2
& 1)) {
1812 /* round to nearest integer for negative values*/
1813 n3
= (int)(pow((double)n1
, (double)n2
) - (double)0.5);
1816 /* round to nearest integer for positive values*/
1817 n3
= (int)(pow((double)n1
, (double)n2
) + (double)0.5);
1821 return errCheck("exponentiation");
1825 ** concatenate two top items on the stack
1826 ** Before: Stack-> str2, str1, next, ...
1827 ** After: Stack-> result, next, ...
1829 static int concat(void)
1831 char *s1
, *s2
, *out
;
1841 out
= AllocString(len1
+ len2
+ 1);
1842 strncpy(out
, s1
, len1
);
1843 strcpy(&out
[len1
], s2
);
1844 PUSH_STRING(out
, len1
+ len2
)
1849 ** Call a subroutine or function (user defined or built-in). Args are the
1850 ** subroutine's symbol, and the number of arguments which have been pushed
1853 ** For a macro subroutine, the return address, frame pointer, number of
1854 ** arguments and space for local variables are added to the stack, and the
1855 ** PC is set to point to the new function. For a built-in routine, the
1856 ** arguments are popped off the stack, and the routine is just called.
1858 ** Before: Prog-> [subrSym], nArgs, next, ...
1859 ** Stack-> argN-arg1, next, ...
1860 ** After: Prog-> next, ... -- (built-in called subr)
1861 ** Stack-> retVal?, next, ...
1862 ** or: Prog-> (in called)next, ... -- (macro code called subr)
1863 ** Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
1865 static int callSubroutine(void)
1869 static DataValue noValue
= {NO_TAG
, {0}};
1873 sym
= (Symbol
*)*PC
++;
1877 STACKDUMP(nArgs
, 3);
1880 ** If the subroutine is built-in, call the built-in routine
1882 if (sym
->type
== C_FUNCTION_SYM
) {
1885 /* "pop" stack back to the first argument in the call stack */
1888 /* Call the function and check for preemption */
1889 PreemptRequest
= False
;
1890 if (!sym
->value
.val
.subr(FocusWindow
, StackP
,
1891 nArgs
, &result
, &errMsg
))
1892 return execError(errMsg
, sym
->name
);
1893 if (*PC
== fetchRetVal
) {
1894 if (result
.tag
== NO_TAG
) {
1895 return execError("%s does not return a value", sym
->name
);
1900 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1904 ** Call a macro subroutine:
1906 ** Push all of the required information to resume, and make space on the
1907 ** stack for local variables (and initialize them), on top of the argument
1908 ** values which are already there.
1910 if (sym
->type
== MACRO_FUNCTION_SYM
) {
1911 StackP
->tag
= NO_TAG
; /* return PC */
1912 StackP
->val
.inst
= PC
;
1915 StackP
->tag
= NO_TAG
; /* old FrameP */
1916 StackP
->val
.dataval
= FrameP
;
1919 StackP
->tag
= NO_TAG
; /* nArgs */
1920 StackP
->val
.n
= nArgs
;
1923 *(StackP
++) = noValue
; /* cached arg array */
1926 prog
= (Program
*)sym
->value
.val
.str
.rep
;
1928 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
1929 FP_GET_SYM_VAL(FrameP
, s
) = noValue
;
1936 ** Call an action routine
1938 if (sym
->type
== ACTION_ROUTINE_SYM
) {
1939 String argList
[MAX_ARGS
];
1940 Cardinal numArgs
= nArgs
;
1941 XKeyEvent key_event
;
1945 /* Create a fake event with a timestamp suitable for actions which need
1946 timestamps, a marker to indicate that the call was from a macro
1947 (to stop shell commands from putting up their own separate banner) */
1948 disp
=XtDisplay(InitiatingWindow
->shell
);
1949 win
=XtWindow(InitiatingWindow
->shell
);
1951 key_event
.type
= KeyPress
;
1952 key_event
.send_event
= MACRO_EVENT_MARKER
;
1953 key_event
.time
=XtLastTimestampProcessed(XtDisplay(InitiatingWindow
->shell
));
1955 /* The following entries are just filled in to avoid problems
1956 in strange cases, like calling "self_insert()" directly from the
1957 macro menu. In fact the display was sufficient to cure this crash. */
1958 key_event
.display
=disp
;
1959 key_event
.window
=key_event
.root
=key_event
.subwindow
=win
;
1961 /* pop arguments off the stack and put them in the argument list */
1962 for (i
=nArgs
-1; i
>=0; i
--) {
1963 POP_STRING(argList
[i
])
1966 /* Call the action routine and check for preemption */
1967 PreemptRequest
= False
;
1968 sym
->value
.val
.xtproc(FocusWindow
->lastFocus
,
1969 (XEvent
*)&key_event
, argList
, &numArgs
);
1970 if (*PC
== fetchRetVal
) {
1971 return execError("%s does not return a value", sym
->name
);
1973 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1976 /* Calling a non subroutine symbol */
1977 return execError("%s is not a function or subroutine", sym
->name
);
1981 ** This should never be executed, returnVal checks for the presence of this
1982 ** instruction at the PC to decide whether to push the function's return
1983 ** value, then skips over it without executing.
1985 static int fetchRetVal(void)
1987 return execError("internal error: frv", NULL
);
1990 /* see comments for returnValOrNone() */
1991 static int returnNoVal(void)
1993 return returnValOrNone(False
);
1995 static int returnVal(void)
1997 return returnValOrNone(True
);
2001 ** Return from a subroutine call
2002 ** Before: Prog-> [next], ...
2003 ** Stack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
2004 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
2005 ** Stack-> retVal?, next, ...
2007 static int returnValOrNone(int valOnStack
)
2010 static DataValue noValue
= {NO_TAG
, {0}};
2011 DataValue
*newFrameP
;
2015 STACKDUMP(StackP
- FrameP
+ FP_GET_ARG_COUNT(FrameP
) + FP_TO_ARGS_DIST
, 3);
2017 /* return value is on the stack */
2022 /* get stored return information */
2023 nArgs
= FP_GET_ARG_COUNT(FrameP
);
2024 newFrameP
= FP_GET_OLD_FP(FrameP
);
2025 PC
= FP_GET_RET_PC(FrameP
);
2027 /* pop past local variables */
2029 /* pop past function arguments */
2030 StackP
-= (FP_TO_ARGS_DIST
+ nArgs
);
2033 /* push returned value, if requsted */
2040 } else if (*PC
== fetchRetVal
) {
2046 "using return value of %s which does not return a value",
2047 ((Symbol
*)*(PC
- 2))->name
);
2051 /* NULL return PC indicates end of program */
2052 return PC
== NULL
? STAT_DONE
: STAT_OK
;
2056 ** Unconditional branch offset by immediate operand
2058 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2059 ** After: Prog-> branchDest, next, ..., (branchdest)[next]
2061 static int branch(void)
2071 ** Conditional branches if stack value is True/False (non-zero/0) to address
2072 ** of immediate operand (pops stack)
2074 ** Before: Prog-> [branchDest], next, ..., (branchdest)next
2075 ** After: either: Prog-> branchDest, [next], ...
2076 ** After: or: Prog-> branchDest, next, ..., (branchdest)[next]
2078 static int branchTrue(void)
2087 addr
= PC
+ (int)*PC
;
2094 static int branchFalse(void)
2103 addr
= PC
+ (int)*PC
;
2112 ** Ignore the address following the instruction and continue. Why? So
2113 ** some code that uses conditional branching doesn't have to figure out
2114 ** whether to store a branch address.
2116 ** Before: Prog-> [branchDest], next, ...
2117 ** After: Prog-> branchDest, [next], ...
2119 static int branchNever(void)
2129 ** recursively copy(duplicate) the sparse array nodes of an array
2130 ** this does not duplicate the key/node data since they are never
2131 ** modified, only replaced
2133 int ArrayCopy(DataValue
*dstArray
, DataValue
*srcArray
)
2135 SparseArrayEntry
*srcIter
;
2137 dstArray
->tag
= ARRAY_TAG
;
2138 dstArray
->val
.arrayPtr
= ArrayNew();
2140 srcIter
= arrayIterateFirst(srcArray
);
2142 if (srcIter
->value
.tag
== ARRAY_TAG
) {
2146 errNum
= ArrayCopy(&tmpArray
, &srcIter
->value
);
2147 if (errNum
!= STAT_OK
) {
2150 if (!ArrayInsert(dstArray
, srcIter
->key
, &tmpArray
)) {
2151 return(execError("array copy failed", NULL
));
2155 if (!ArrayInsert(dstArray
, srcIter
->key
, &srcIter
->value
)) {
2156 return(execError("array copy failed", NULL
));
2159 srcIter
= arrayIterateNext(srcIter
);
2165 ** creates an allocated string of a single key for all the sub-scripts
2166 ** using ARRAY_DIM_SEP as a separator
2167 ** this function uses the PEEK macros in order to remove most limits on
2168 ** the number of arguments to an array
2169 ** I really need to optimize the size approximation rather than assuming
2170 ** a worst case size for every integer argument
2172 static int makeArrayKeyFromArgs(int nArgs
, char **keyString
, int leaveParams
)
2175 int sepLen
= strlen(ARRAY_DIM_SEP
);
2179 keyLength
= sepLen
* (nArgs
- 1);
2180 for (i
= nArgs
- 1; i
>= 0; --i
) {
2182 if (tmpVal
.tag
== INT_TAG
) {
2183 keyLength
+= TYPE_INT_STR_SIZE(tmpVal
.val
.n
);
2185 else if (tmpVal
.tag
== STRING_TAG
) {
2186 keyLength
+= tmpVal
.val
.str
.len
;
2189 return(execError("can only index array with string or int.", NULL
));
2192 *keyString
= AllocString(keyLength
+ 1);
2193 (*keyString
)[0] = 0;
2194 for (i
= nArgs
- 1; i
>= 0; --i
) {
2195 if (i
!= nArgs
- 1) {
2196 strcat(*keyString
, ARRAY_DIM_SEP
);
2199 if (tmpVal
.tag
== INT_TAG
) {
2200 sprintf(&((*keyString
)[strlen(*keyString
)]), "%d", tmpVal
.val
.n
);
2202 else if (tmpVal
.tag
== STRING_TAG
) {
2203 strcat(*keyString
, tmpVal
.val
.str
.rep
);
2206 return(execError("can only index array with string or int.", NULL
));
2210 for (i
= nArgs
- 1; i
>= 0; --i
) {
2218 ** allocate an empty array node, this is used as the root node and never
2219 ** contains any data, only refernces to other nodes
2221 static rbTreeNode
*arrayEmptyAllocator(void)
2223 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
2225 newNode
->key
= NULL
;
2226 newNode
->value
.tag
= NO_TAG
;
2228 return((rbTreeNode
*)newNode
);
2232 ** create and copy array node and copy contents, we merely copy pointers
2233 ** since they are never modified, only replaced
2235 static rbTreeNode
*arrayAllocateNode(rbTreeNode
*src
)
2237 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
2239 newNode
->key
= ((SparseArrayEntry
*)src
)->key
;
2240 newNode
->value
= ((SparseArrayEntry
*)src
)->value
;
2242 return((rbTreeNode
*)newNode
);
2246 ** copy array node data, we merely copy pointers since they are never
2247 ** modified, only replaced
2249 static int arrayEntryCopyToNode(rbTreeNode
*dst
, rbTreeNode
*src
)
2251 ((SparseArrayEntry
*)dst
)->key
= ((SparseArrayEntry
*)src
)->key
;
2252 ((SparseArrayEntry
*)dst
)->value
= ((SparseArrayEntry
*)src
)->value
;
2257 ** compare two array nodes returning an integer value similar to strcmp()
2259 static int arrayEntryCompare(rbTreeNode
*left
, rbTreeNode
*right
)
2261 return(strcmp(((SparseArrayEntry
*)left
)->key
, ((SparseArrayEntry
*)right
)->key
));
2265 ** dispose an array node, garbage collection handles this, so we mark it
2266 ** to allow iterators in macro language to determine they have been unlinked
2268 static void arrayDisposeNode(rbTreeNode
*src
)
2270 /* Let garbage collection handle this but mark it so iterators can tell */
2277 struct SparseArrayEntry
*ArrayNew(void)
2279 return((struct SparseArrayEntry
*)rbTreeNew(arrayEmptyAllocator
));
2283 ** insert a DataValue into an array, allocate the array if needed
2284 ** keyStr must be a string that was allocated with AllocString()
2286 int ArrayInsert(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
2288 SparseArrayEntry tmpEntry
;
2289 rbTreeNode
*insertedNode
;
2291 tmpEntry
.key
= keyStr
;
2292 tmpEntry
.value
= *theValue
;
2294 if (theArray
->val
.arrayPtr
== NULL
) {
2295 theArray
->val
.arrayPtr
= ArrayNew();
2297 if (theArray
->val
.arrayPtr
!= NULL
) {
2298 insertedNode
= rbTreeInsert((rbTreeNode
*)(theArray
->val
.arrayPtr
),
2299 (rbTreeNode
*)&tmpEntry
,
2300 arrayEntryCompare
, arrayAllocateNode
, arrayEntryCopyToNode
);
2312 ** remove a node from an array whose key matches keyStr
2314 void ArrayDelete(DataValue
*theArray
, char *keyStr
)
2316 SparseArrayEntry searchEntry
;
2318 if (theArray
->val
.arrayPtr
) {
2319 searchEntry
.key
= keyStr
;
2320 rbTreeDelete((rbTreeNode
*)theArray
->val
.arrayPtr
, (rbTreeNode
*)&searchEntry
,
2321 arrayEntryCompare
, arrayDisposeNode
);
2326 ** remove all nodes from an array
2328 void ArrayDeleteAll(DataValue
*theArray
)
2331 if (theArray
->val
.arrayPtr
) {
2332 rbTreeNode
*iter
= rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
2334 rbTreeNode
*nextIter
= rbTreeNext(iter
);
2335 rbTreeDeleteNode((rbTreeNode
*)theArray
->val
.arrayPtr
,
2336 iter
, arrayDisposeNode
);
2344 ** returns the number of elements (nodes containing values) of an array
2346 int ArraySize(DataValue
*theArray
)
2348 if (theArray
->val
.arrayPtr
) {
2349 return(rbTreeSize((rbTreeNode
*)theArray
->val
.arrayPtr
));
2357 ** retrieves an array node whose key matches
2358 ** returns 1 for success 0 for not found
2360 int ArrayGet(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
2362 SparseArrayEntry searchEntry
;
2363 rbTreeNode
*foundNode
;
2365 if (theArray
->val
.arrayPtr
) {
2366 searchEntry
.key
= keyStr
;
2367 foundNode
= rbTreeFind((rbTreeNode
*)theArray
->val
.arrayPtr
,
2368 (rbTreeNode
*)&searchEntry
, arrayEntryCompare
);
2370 *theValue
= ((SparseArrayEntry
*)foundNode
)->value
;
2378 ** get pointer to start iterating an array
2380 SparseArrayEntry
*arrayIterateFirst(DataValue
*theArray
)
2382 SparseArrayEntry
*startPos
;
2383 if (theArray
->val
.arrayPtr
) {
2384 startPos
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
2393 ** move iterator to next entry in array
2395 SparseArrayEntry
*arrayIterateNext(SparseArrayEntry
*iterator
)
2397 SparseArrayEntry
*nextPos
;
2399 nextPos
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)iterator
);
2408 ** evaluate an array element and push the result onto the stack
2410 ** Before: Prog-> [nDim], next, ...
2411 ** Stack-> indnDim, ... ind1, ArraySym, next, ...
2412 ** After: Prog-> nDim, [next], ...
2413 ** Stack-> indexedArrayVal, next, ...
2415 static int arrayRef(void)
2418 DataValue srcArray
, valueItem
;
2419 char *keyString
= NULL
;
2429 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2430 if (errNum
!= STAT_OK
) {
2435 if (srcArray
.tag
== ARRAY_TAG
) {
2436 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2437 return(execError("referenced array value not in array: %s", keyString
));
2443 return(execError("operator [] on non-array", NULL
));
2448 if (srcArray
.tag
== ARRAY_TAG
) {
2449 PUSH_INT(ArraySize(&srcArray
))
2453 return(execError("operator [] on non-array", NULL
));
2459 ** assign to an array element of a referenced array on the stack
2461 ** Before: Prog-> [nDim], next, ...
2462 ** Stack-> rhs, indnDim, ... ind1, ArraySym, next, ...
2463 ** After: Prog-> nDim, [next], ...
2464 ** Stack-> next, ...
2466 static int arrayAssign(void)
2468 char *keyString
= NULL
;
2469 DataValue srcValue
, dstArray
;
2482 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2483 if (errNum
!= STAT_OK
) {
2489 if (dstArray
.tag
!= ARRAY_TAG
&& dstArray
.tag
!= NO_TAG
) {
2490 return(execError("cannot assign array element of non-array", NULL
));
2492 if (srcValue
.tag
== ARRAY_TAG
) {
2493 DataValue arrayCopyValue
;
2495 errNum
= ArrayCopy(&arrayCopyValue
, &srcValue
);
2496 srcValue
= arrayCopyValue
;
2497 if (errNum
!= STAT_OK
) {
2501 if (ArrayInsert(&dstArray
, keyString
, &srcValue
)) {
2505 return(execError("array member allocation failure", NULL
));
2508 return(execError("empty operator []", NULL
));
2512 ** for use with assign-op operators (eg a[i,j] += k
2514 ** Before: Prog-> [binOp], nDim, next, ...
2515 ** Stack-> [rhs], indnDim, ... ind1, next, ...
2516 ** After: Prog-> binOp, nDim, [next], ...
2517 ** Stack-> [rhs], arrayValue, next, ...
2519 static int arrayRefAndAssignSetup(void)
2522 DataValue srcArray
, valueItem
, moveExpr
;
2523 char *keyString
= NULL
;
2526 binaryOp
= (int)*PC
;
2532 STACKDUMP(nDim
+ 1, 3);
2539 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 1);
2540 if (errNum
!= STAT_OK
) {
2544 PEEK(srcArray
, nDim
)
2545 if (srcArray
.tag
== ARRAY_TAG
) {
2546 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2547 return(execError("referenced array value not in array: %s", keyString
));
2556 return(execError("operator [] on non-array", NULL
));
2560 return(execError("array[] not an lvalue", NULL
));
2565 ** setup symbol values for array iteration in interpreter
2567 ** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ...
2568 ** Stack-> [arrayVal], next, ...
2569 ** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ...
2570 ** Stack-> [next], ...
2572 ** iter is a symbol which gives the position of the iterator value in
2574 ** arrayVal is the data value holding the array in question
2576 static int beginArrayIter(void)
2579 DataValue
*iteratorValPtr
;
2585 iterator
= (Symbol
*)*PC
;
2590 if (iterator
->type
== LOCAL_SYM
) {
2591 iteratorValPtr
= &FP_GET_SYM_VAL(FrameP
, iterator
);
2594 return(execError("bad temporary iterator: %s", iterator
->name
));
2597 iteratorValPtr
->tag
= INT_TAG
;
2598 if (arrayVal
.tag
!= ARRAY_TAG
) {
2599 return(execError("can't iterate non-array", NULL
));
2602 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateFirst(&arrayVal
);
2607 ** copy key to symbol if node is still valid, marked bad by a color of -1
2608 ** then move iterator to next node
2609 ** this allows iterators to progress even if you delete any node in the array
2610 ** except the item just after the current key
2612 ** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ...
2613 ** Stack-> [next], ...
2614 ** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ...
2615 ** Stack-> [next], ... (unchanged)
2617 ** iter is a symbol which gives the position of the iterator value in
2618 ** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers
2619 ** to the array and a position within it
2620 ** iterVal is the programmer-visible symbol which will take the current
2622 ** endLoopBranch is the instruction offset to the instruction following the
2623 ** loop (measured from itself)
2624 ** arrayVal is the data value holding the array in question
2625 ** The return-to-start-of-loop branch (at the end of the loop) should address
2626 ** the ARRAY_ITER instruction
2628 static int arrayIter(void)
2632 DataValue
*iteratorValPtr
;
2633 DataValue
*itemValPtr
;
2634 SparseArrayEntry
*thisEntry
;
2640 item
= (Symbol
*)*PC
;
2642 iterator
= (Symbol
*)*PC
;
2644 branchAddr
= PC
+ (int)*PC
;
2647 if (item
->type
== LOCAL_SYM
) {
2648 itemValPtr
= &FP_GET_SYM_VAL(FrameP
, item
);
2650 else if (item
->type
== GLOBAL_SYM
) {
2651 itemValPtr
= &(item
->value
);
2654 return(execError("can't assign to: %s", item
->name
));
2656 itemValPtr
->tag
= NO_TAG
;
2658 if (iterator
->type
== LOCAL_SYM
) {
2659 iteratorValPtr
= &FP_GET_SYM_VAL(FrameP
, iterator
);
2662 return(execError("bad temporary iterator: %s", iterator
->name
));
2665 thisEntry
= (SparseArrayEntry
*)iteratorValPtr
->val
.arrayPtr
;
2666 if (thisEntry
&& thisEntry
->nodePtrs
.color
!= -1) {
2667 itemValPtr
->tag
= STRING_TAG
;
2668 itemValPtr
->val
.str
.rep
= thisEntry
->key
;
2669 itemValPtr
->val
.str
.len
= strlen(thisEntry
->key
);
2671 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateNext(thisEntry
);
2680 ** determine if a key or keys exists in an array
2681 ** if the left argument is a string or integer a single check is performed
2682 ** if the key exists, 1 is pushed onto the stack, otherwise 0
2683 ** if the left argument is an array 1 is pushed onto the stack if every key
2684 ** in the left array exists in the right array, otherwise 0
2686 ** Before: Prog-> [next], ...
2687 ** Stack-> [ArraySym], inSymbol, next, ...
2688 ** After: Prog-> [next], ... -- (unchanged)
2689 ** Stack-> next, ...
2691 static int inArray(void)
2693 DataValue theArray
, leftArray
, theValue
;
2701 if (theArray
.tag
!= ARRAY_TAG
) {
2702 return(execError("operator in on non-array", NULL
));
2705 if (leftArray
.tag
== ARRAY_TAG
) {
2706 SparseArrayEntry
*iter
;
2710 iter
= arrayIterateFirst(&leftArray
);
2711 while (inResult
&& iter
) {
2712 inResult
= inResult
&& ArrayGet(&theArray
, iter
->key
, &theValue
);
2713 iter
= arrayIterateNext(iter
);
2718 if (ArrayGet(&theArray
, keyStr
, &theValue
)) {
2727 ** remove a given key from an array unless nDim is 0, then all keys are removed
2729 ** for use with assign-op operators (eg a[i,j] += k
2730 ** Before: Prog-> [nDim], next, ...
2731 ** Stack-> [indnDim], ... ind1, arrayValue, next, ...
2732 ** After: Prog-> nDim, [next], ...
2733 ** Stack-> next, ...
2735 static int deleteArrayElement(void)
2738 char *keyString
= NULL
;
2745 STACKDUMP(nDim
+ 1, 3);
2750 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
, 0);
2751 if (errNum
!= STAT_OK
) {
2757 if (theArray
.tag
== ARRAY_TAG
) {
2759 ArrayDelete(&theArray
, keyString
);
2762 ArrayDeleteAll(&theArray
);
2766 return(execError("attempt to delete from non-array", NULL
));
2772 ** checks errno after operations which can set it. If an error occured,
2773 ** creates appropriate error messages and returns false
2775 static int errCheck(const char *s
)
2778 return execError("%s argument out of domain", s
);
2779 else if (errno
== ERANGE
)
2780 return execError("%s result out of range", s
);
2786 ** combine two strings in a static area and set ErrMsg to point to the
2787 ** result. Returns false so a single return execError() statement can
2788 ** be used to both process the message and return.
2790 static int execError(const char *s1
, const char *s2
)
2792 static char msg
[MAX_ERR_MSG_LEN
];
2794 sprintf(msg
, s1
, s2
);
2799 int StringToNum(const char *string
, int *number
)
2801 const char *c
= string
;
2803 while (*c
== ' ' || *c
== '\t') {
2806 if (*c
== '+' || *c
== '-') {
2809 while (isdigit((unsigned char)*c
)) {
2812 while (*c
== ' ' || *c
== '\t') {
2816 /* if everything went as expected, we should be at end, but we're not */
2820 if (sscanf(string
, "%d", number
) != 1) {
2821 /* This case is here to support old behavior */
2828 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */
2829 static void dumpVal(DataValue dv
)
2833 printf("i=%d", dv
.val
.n
);
2839 char *src
= dv
.val
.str
.rep
;
2844 for (k
= 0; src
[k
] && k
< sizeof s
- 1; k
++) {
2845 s
[k
] = isprint(src
[k
]) ? src
[k
] : '?';
2848 printf("s=\"%s\"%s[%d]", s
,
2849 src
[k
] ? "..." : "", strlen(src
));
2858 printf("<no value>");
2861 printf("?%8p", dv
.val
.inst
);
2865 printf("UNKNOWN DATA TAG %d ?%8p", dv
.tag
, dv
.val
.inst
);
2869 #endif /* #ifdef DEBUG_DISASSEMBLER */
2871 #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */
2872 static void disasm(Inst
*inst
, int nInstr
)
2874 static const char *opNames
[N_OPS
] = {
2875 "RETURN_NO_VAL", /* returnNoVal */
2876 "RETURN", /* returnVal */
2877 "PUSH_SYM", /* pushSymVal */
2878 "DUP", /* dupStack */
2880 "SUB", /* subtract */
2881 "MUL", /* multiply */
2884 "NEGATE", /* negate */
2885 "INCR", /* increment */
2886 "DECR", /* decrement */
2893 "BIT_AND", /* bitAnd */
2894 "BIT_OR", /* bitOr */
2898 "POWER", /* power */
2899 "CONCAT", /* concat */
2900 "ASSIGN", /* assign */
2901 "SUBR_CALL", /* callSubroutine */
2902 "FETCH_RET_VAL", /* fetchRetVal */
2903 "BRANCH", /* branch */
2904 "BRANCH_TRUE", /* branchTrue */
2905 "BRANCH_FALSE", /* branchFalse */
2906 "BRANCH_NEVER", /* branchNever */
2907 "ARRAY_REF", /* arrayRef */
2908 "ARRAY_ASSIGN", /* arrayAssign */
2909 "BEGIN_ARRAY_ITER", /* beginArrayIter */
2910 "ARRAY_ITER", /* arrayIter */
2911 "IN_ARRAY", /* inArray */
2912 "ARRAY_DELETE", /* deleteArrayElement */
2913 "PUSH_ARRAY_SYM", /* pushArraySymVal */
2914 "ARRAY_REF_ASSIGN_SETUP", /* arrayRefAndAssignSetup */
2915 "PUSH_ARG", /* $arg[expr] */
2916 "PUSH_ARG_COUNT", /* $arg[] */
2917 "PUSH_ARG_ARRAY" /* $arg */
2922 for (i
= 0; i
< nInstr
; ++i
) {
2923 printf("Prog %8p ", &inst
[i
]);
2924 for (j
= 0; j
< N_OPS
; ++j
) {
2925 if (inst
[i
] == OpFns
[j
]) {
2926 printf("%22s ", opNames
[j
]);
2927 if (j
== OP_PUSH_SYM
|| j
== OP_ASSIGN
) {
2928 Symbol
*sym
= (Symbol
*)inst
[i
+1];
2929 printf("%s", sym
->name
);
2930 if (sym
->value
.tag
== STRING_TAG
&&
2931 strncmp(sym
->name
, "string #", 8) == 0) {
2932 dumpVal(sym
->value
);
2936 else if (j
== OP_BRANCH
|| j
== OP_BRANCH_FALSE
||
2937 j
== OP_BRANCH_NEVER
|| j
== OP_BRANCH_TRUE
) {
2938 printf("to=(%d) %x", (int)inst
[i
+1],
2939 (int)(&inst
[i
+1] + (int)inst
[i
+1]));
2942 else if (j
== OP_SUBR_CALL
) {
2943 printf("%s (%d arg)", ((Symbol
*)inst
[i
+1])->name
,
2947 else if (j
== OP_BEGIN_ARRAY_ITER
) {
2949 ((Symbol
*)inst
[i
+1])->name
);
2952 else if (j
== OP_ARRAY_ITER
) {
2953 printf("%s = %s++ end-loop=(%d) %x",
2954 ((Symbol
*)inst
[i
+1])->name
,
2955 ((Symbol
*)inst
[i
+2])->name
,
2957 (int)(&inst
[i
+3] + (int)inst
[i
+3]));
2960 else if (j
== OP_ARRAY_REF
|| j
== OP_ARRAY_DELETE
||
2961 j
== OP_ARRAY_ASSIGN
) {
2966 else if (j
== OP_ARRAY_REF_ASSIGN_SETUP
) {
2968 ((int)inst
[i
+1]) ? "true" : "false");
2973 else if (j
== OP_PUSH_ARRAY_SYM
) {
2974 printf("%s", ((Symbol
*)inst
[++i
])->name
);
2976 (int)inst
[i
+1] ? "createAndRef" : "refOnly");
2985 printf("%x\n", (int)inst
[i
]);
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 /* Stack-> 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];
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; /* number of arguments */
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", offset
+ FP_TO_ARGS_DIST
+ nArgs
+ 1);
3023 printf("%-6s ", pos
);
3028 #endif /* ifdef DEBUG_STACK */