1 static const char CVSID
[] = "$Id: interpret.c,v 1.27 2002/08/10 23:48:55 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 *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
33 #include "interpret.h"
48 #include "../util/VMSparam.h"
51 #include <sys/param.h>
55 #include <X11/Intrinsic.h>
62 #define PROGRAM_SIZE 4096 /* Maximum program size */
63 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
64 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
65 allowed per program */
66 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
67 allowed to execute before preempting and
68 returning to allow other things to run */
70 /* Temporary markers placed in a branch address location to designate
71 which loop address (break or continue) the location needs */
72 #define NEEDS_BREAK (Inst)1
73 #define NEEDS_CONTINUE (Inst)2
75 #define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */
77 enum opStatusCodes
{STAT_OK
=2, STAT_DONE
, STAT_ERROR
, STAT_PREEMPT
};
79 static void addLoopAddr(Inst
*addr
);
80 static void saveContext(RestartData
*context
);
81 static void restoreContext(RestartData
*context
);
82 static int returnNoVal(void);
83 static int returnVal(void);
84 static int returnValOrNone(int valOnStack
);
85 static int pushSymVal(void);
86 static int dupStack(void);
88 static int subtract(void);
89 static int multiply(void);
90 static int divide(void);
91 static int modulo(void);
92 static int negate(void);
93 static int increment(void);
94 static int decrement(void);
101 static int bitAnd(void);
102 static int bitOr(void);
103 static int and(void);
105 static int not(void);
106 static int power(void);
107 static int concat(void);
108 static int assign(void);
109 static int callSubroutine(void);
110 static int fetchRetVal(void);
111 static int branch(void);
112 static int branchTrue(void);
113 static int branchFalse(void);
114 static int branchNever(void);
115 static int arrayRef(void);
116 static int arrayAssign(void);
117 static int beginArrayIter(void);
118 static int arrayIter(void);
119 static int inArray(void);
120 static int deleteArrayElement(void);
121 static void freeSymbolTable(Symbol
*symTab
);
122 static int errCheck(const char *s
);
123 static int execError(const char *s1
, const char *s2
);
124 static int stringToNum(const char *string
, int *number
);
125 static rbTreeNode
*arrayEmptyAllocator(void);
126 static rbTreeNode
*arrayAllocateNode(rbTreeNode
*src
);
127 static int arrayEntryCopyToNode(rbTreeNode
*dst
, rbTreeNode
*src
);
128 static int arrayEntryCompare(rbTreeNode
*left
, rbTreeNode
*right
);
129 static void arrayDisposeNode(rbTreeNode
*src
);
130 static SparseArrayEntry
*allocateSparseArrayEntry(void);
131 /* #define DEBUG_ASSEMBLY */
132 #ifdef DEBUG_ASSEMBLY
133 static void disasm(Program
*prog
, int nInstr
);
136 /* Global symbols and function definitions */
137 static Symbol
*GlobalSymList
= NULL
;
139 /* List of all memory allocated for strings */
140 static char *AllocatedStrings
= NULL
;
143 SparseArrayEntry data
; /* LEAVE this as top entry */
144 int inUse
; /* we use pointers to the data to refer to the entire struct */
145 struct SparseArrayEntryWrapper
*next
;
146 } SparseArrayEntryWrapper
;
148 static SparseArrayEntryWrapper
*AllocatedSparseArrayEntries
= NULL
;
150 /* Message strings used in macros (so they don't get repeated every time
151 the macros are used */
152 static const char *StackOverflowMsg
= "macro stack overflow";
153 static const char *StackUnderflowMsg
= "macro stack underflow";
154 static const char *StringToNumberMsg
= "string could not be converted to number";
156 /* Temporary global data for use while accumulating programs */
157 static Symbol
*LocalSymList
= NULL
; /* symbols local to the program */
158 static Inst Prog
[PROGRAM_SIZE
]; /* the program */
159 static Inst
*ProgP
; /* next free spot for code gen. */
160 static Inst
*LoopStack
[LOOP_STACK_SIZE
]; /* addresses of break, cont stmts */
161 static Inst
**LoopStackPtr
= LoopStack
; /* to fill at the end of a loop */
163 /* Global data for the interpreter */
164 static DataValue
*Stack
; /* the stack */
165 static DataValue
*StackP
; /* next free spot on stack */
166 static DataValue
*FrameP
; /* frame pointer (start of local variables
167 for the current subroutine invocation) */
168 static Inst
*PC
; /* program counter during execution */
169 static char *ErrMsg
; /* global for returning error messages
170 from executing functions */
172 *InitiatingWindow
= NULL
; /* window from which macro was run */
173 static WindowInfo
*FocusWindow
; /* window on which macro commands operate */
174 static int PreemptRequest
; /* passes preemption requests from called
175 routines back up to the interpreter */
177 /* Array for mapping operations to functions for performing the operations
178 Must correspond to the enum called "operations" in interpret.h */
179 static int (*OpFns
[N_OPS
])() = {returnNoVal
, returnVal
, pushSymVal
, dupStack
,
180 add
, subtract
, multiply
, divide
, modulo
, negate
, increment
, decrement
,
181 gt
, lt
, ge
, le
, eq
, ne
, bitAnd
, bitOr
, and, or, not, power
, concat
,
182 assign
, callSubroutine
, fetchRetVal
, branch
, branchTrue
, branchFalse
,
183 branchNever
, arrayRef
, arrayAssign
, beginArrayIter
, arrayIter
, inArray
,
187 ** Initialize macro language global variables. Must be called before
188 ** any macros are even parsed, because the parser uses action routine
189 ** symbols to comprehend hyphenated names.
191 void InitMacroGlobals(void)
193 XtActionsRec
*actions
;
195 static char argName
[3] = "$x";
196 static DataValue dv
= {NO_TAG
, {0}};
198 /* Add action routines from NEdit menus and text widget */
199 actions
= GetMenuActions(&nActions
);
200 for (i
=0; i
<nActions
; i
++) {
201 dv
.val
.xtproc
= actions
[i
].proc
;
202 InstallSymbol(actions
[i
].string
, ACTION_ROUTINE_SYM
, dv
);
204 actions
= TextGetActions(&nActions
);
205 for (i
=0; i
<nActions
; i
++) {
206 dv
.val
.xtproc
= actions
[i
].proc
;
207 InstallSymbol(actions
[i
].string
, ACTION_ROUTINE_SYM
, dv
);
210 /* Add subroutine argument symbols ($1, $2, ..., $9) */
211 for (i
=0; i
<9; i
++) {
212 argName
[1] = '1' + i
;
214 InstallSymbol(argName
, ARG_SYM
, dv
);
217 /* Add special symbol $n_args */
218 dv
.val
.n
= N_ARGS_ARG_SYM
;
219 InstallSymbol("$n_args", ARG_SYM
, dv
);
223 ** To build a program for the interpreter, call BeginCreatingProgram, to
224 ** begin accumulating the program, followed by calls to AddOp, AddSym,
225 ** and InstallSymbol to add symbols and operations. When the new program
226 ** is finished, collect the results with FinishCreatingProgram. This returns
227 ** a self contained program that can be run with ExecuteMacro.
231 ** Start collecting instructions for a program. Clears the program
232 ** and the symbol table.
234 void BeginCreatingProgram(void)
238 LoopStackPtr
= LoopStack
;
242 ** Finish up the program under construction, and return it (code and
243 ** symbol table) as a package that ExecuteMacro can execute. This
244 ** program must be freed with FreeProgram.
246 Program
*FinishCreatingProgram(void)
249 int progLen
, fpOffset
= 0;
252 newProg
= (Program
*)XtMalloc(sizeof(Program
));
253 progLen
= ((char *)ProgP
) - ((char *)Prog
);
254 newProg
->code
= (Inst
*)XtMalloc(progLen
);
255 memcpy(newProg
->code
, Prog
, progLen
);
256 newProg
->localSymList
= LocalSymList
;
259 /* Local variables' values are stored on the stack. Here we assign
260 frame pointer offsets to them. */
261 for (s
= newProg
->localSymList
; s
!= NULL
; s
= s
->next
)
262 s
->value
.val
.n
= fpOffset
++;
264 #ifdef DEBUG_ASSEMBLY
265 disasm(newProg
, ProgP
- Prog
);
271 void FreeProgram(Program
*prog
)
273 freeSymbolTable(prog
->localSymList
);
274 XtFree((char *)prog
->code
);
275 XtFree((char *)prog
);
279 ** Add an operator (instruction) to the end of the current program
281 int AddOp(int op
, char **msg
)
283 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
284 *msg
= "macro too large";
287 *ProgP
++ = OpFns
[op
];
292 ** Add a symbol operand to the current program
294 int AddSym(Symbol
*sym
, char **msg
)
296 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
297 *msg
= "macro too large";
300 *ProgP
++ = (Inst
)sym
;
305 ** Add an immediate value operand to the current program
307 int AddImmediate(void *value
, char **msg
)
309 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
310 *msg
= "macro too large";
313 *ProgP
++ = (Inst
)value
;
318 ** Add a branch offset operand to the current program
320 int AddBranchOffset(Inst
*to
, char **msg
)
322 if (ProgP
>= &Prog
[PROGRAM_SIZE
]) {
323 *msg
= "macro too large";
326 *ProgP
= (Inst
)(to
- ProgP
);
333 ** Return the address at which the next instruction will be stored
341 ** Swap the positions of two contiguous blocks of code. The first block
342 ** running between locations start and boundary, and the second between
345 void SwapCode(Inst
*start
, Inst
*boundary
, Inst
*end
)
349 temp
= XtMalloc((boundary
- start
) * sizeof(Inst
*));
350 memcpy(temp
, start
, (boundary
-start
) * sizeof(Inst
*));
351 memmove(start
, boundary
, (end
-boundary
) * sizeof(Inst
*));
352 memcpy(start
+(end
-boundary
), temp
, (boundary
-start
) * sizeof(Inst
*));
357 ** Maintain a stack to save addresses of branch operations for break and
358 ** continue statements, so they can be filled in once the information
359 ** on where to branch is known.
361 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or
362 ** AddContinueAddr to register the address at which to store the branch
363 ** address for a break or continue statement, and FillLoopAddrs to fill
364 ** in all the addresses and return to the level of the enclosing loop.
366 void StartLoopAddrList(void)
371 int AddBreakAddr(Inst
*addr
)
373 if (LoopStackPtr
== LoopStack
) return 1;
379 int AddContinueAddr(Inst
*addr
)
381 if (LoopStackPtr
== LoopStack
) return 1;
383 *addr
= NEEDS_CONTINUE
;
387 static void addLoopAddr(Inst
*addr
)
389 if (LoopStackPtr
> &LoopStack
[LOOP_STACK_SIZE
-1]) {
390 fprintf(stderr
, "NEdit: loop stack overflow in macro parser");
393 *LoopStackPtr
++ = addr
;
396 void FillLoopAddrs(Inst
*breakAddr
, Inst
*continueAddr
)
400 if (LoopStackPtr
< LoopStack
) {
401 fprintf(stderr
, "NEdit: internal error (lsu) in macro parser\n");
404 if (*LoopStackPtr
== NULL
)
406 if (**LoopStackPtr
== NEEDS_BREAK
)
407 **(Inst
***)LoopStackPtr
= (Inst
*)(breakAddr
- *LoopStackPtr
);
408 else if (**LoopStackPtr
== NEEDS_CONTINUE
)
409 **(Inst
***)LoopStackPtr
= (Inst
*)(continueAddr
- *LoopStackPtr
);
411 fprintf(stderr
, "NEdit: internal error (uat) in macro parser\n");
416 ** Execute a compiled macro, "prog", using the arguments in the array
417 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.
418 ** if MACRO_DONE is returned, the macro completed, and the returned value
419 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the
420 ** macro exceeded its alotted time-slice and scheduled...
422 int ExecuteMacro(WindowInfo
*window
, Program
*prog
, int nArgs
, DataValue
*args
,
423 DataValue
*result
, RestartData
**continuation
, char **msg
)
425 RestartData
*context
;
426 static DataValue noValue
= {NO_TAG
, {0}};
430 /* Create an execution context (a stack, a stack pointer, a frame pointer,
431 and a program counter) which will retain the program state across
432 preemption and resumption of execution */
433 context
= (RestartData
*)XtMalloc(sizeof(RestartData
));
434 context
->stack
= (DataValue
*)XtMalloc(sizeof(DataValue
) * STACK_SIZE
);
435 *continuation
= context
;
436 context
->stackP
= context
->stack
;
437 context
->pc
= prog
->code
;
438 context
->runWindow
= window
;
439 context
->focusWindow
= window
;
441 /* Push arguments and call information onto the stack */
442 for (i
=0; i
<nArgs
; i
++)
443 *(context
->stackP
++) = args
[i
];
444 context
->stackP
->val
.subr
= NULL
;
445 context
->stackP
->tag
= NO_TAG
;
447 *(context
->stackP
++) = noValue
;
448 context
->stackP
->tag
= NO_TAG
;
449 context
->stackP
->val
.n
= nArgs
;
451 context
->frameP
= context
->stackP
;
453 /* Initialize and make room on the stack for local variables */
454 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
455 *(context
->frameP
+ s
->value
.val
.n
) = noValue
;
459 /* Begin execution, return on error or preemption */
460 return ContinueMacro(context
, result
, msg
);
464 ** Continue the execution of a suspended macro whose state is described in
467 int ContinueMacro(RestartData
*continuation
, DataValue
*result
, char **msg
)
469 register int status
, instCount
= 0;
471 RestartData oldContext
;
473 /* To allow macros to be invoked arbitrarily (such as those automatically
474 triggered within smart-indent) within executing macros, this call is
476 saveContext(&oldContext
);
479 ** Execution Loop: Call the succesive routine addresses in the program
480 ** until one returns something other than STAT_OK, then take action
482 restoreContext(continuation
);
486 /* Execute an instruction */
490 /* If error return was not STAT_OK, return to caller */
491 if (status
!= STAT_OK
) {
492 if (status
== STAT_PREEMPT
) {
493 saveContext(continuation
);
494 restoreContext(&oldContext
);
495 return MACRO_PREEMPT
;
496 } else if (status
== STAT_ERROR
) {
498 FreeRestartData(continuation
);
499 restoreContext(&oldContext
);
501 } else if (status
== STAT_DONE
) {
504 FreeRestartData(continuation
);
505 restoreContext(&oldContext
);
510 /* Count instructions executed. If the instruction limit is hit,
511 preempt, store re-start information in continuation and give
512 X, other macros, and other shell scripts a chance to execute */
514 if (instCount
>= INSTRUCTION_LIMIT
) {
515 saveContext(continuation
);
516 restoreContext(&oldContext
);
517 return MACRO_TIME_LIMIT
;
523 ** If a macro is already executing, and requests that another macro be run,
524 ** this can be called instead of ExecuteMacro to run it in the same context
525 ** as if it were a subroutine. This saves the caller from maintaining
526 ** separate contexts, and serializes processing of the two macros without
529 void RunMacroAsSubrCall(Program
*prog
)
532 static DataValue noValue
= {NO_TAG
, {0}};
534 /* See subroutine "call" for a description of the stack frame for a
536 StackP
->tag
= NO_TAG
;
537 StackP
->val
.inst
= PC
;
539 StackP
->tag
= NO_TAG
;
540 StackP
->val
.dataval
= FrameP
;
542 StackP
->tag
= NO_TAG
;
547 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
548 *(FrameP
+ s
->value
.val
.n
) = noValue
;
553 void FreeRestartData(RestartData
*context
)
555 XtFree((char *)context
->stack
);
556 XtFree((char *)context
);
560 ** Cause a macro in progress to be preempted (called by commands which take
561 ** a long time, or want to return to the event loop. Call ResumeMacroExecution
564 void PreemptMacro(void)
566 PreemptRequest
= True
;
570 ** Reset the return value for a subroutine which caused preemption (this is
571 ** how to return a value from a routine which preempts instead of returning
572 ** a value directly).
574 void ModifyReturnedValue(RestartData
*context
, DataValue dv
)
576 if (*(context
->pc
-1) == fetchRetVal
)
577 *(context
->stackP
-1) = dv
;
581 ** Called within a routine invoked from a macro, returns the window in
582 ** which the macro is executing (where the banner is, not where it is focused)
584 WindowInfo
*MacroRunWindow(void)
586 return InitiatingWindow
;
590 ** Called within a routine invoked from a macro, returns the window to which
591 ** the currently executing macro is focused (the window which macro commands
592 ** modify, not the window from which the macro is being run)
594 WindowInfo
*MacroFocusWindow(void)
600 ** Set the window to which macro subroutines and actions which operate on an
601 ** implied window are directed.
603 void SetMacroFocusWindow(WindowInfo
*window
)
605 FocusWindow
= window
;
609 ** install an array iteration symbol
610 ** it is tagged as an integer but holds an array node pointer
612 Symbol
*InstallIteratorSymbol()
614 char symbolName
[10 + (sizeof(int) * 3) + 2];
616 static int interatorNameIndex
= 0;
618 sprintf(symbolName
, "aryiter #%d", interatorNameIndex
);
619 ++interatorNameIndex
;
621 value
.val
.arrayPtr
= NULL
;
622 return(InstallSymbol(symbolName
, LOCAL_SYM
, value
));
626 ** Lookup a constant string by it's value. This allows reuse of string
627 ** constants and fixing a leak in the interpreter.
629 Symbol
*LookupStringConstSymbol(const char *value
)
633 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
) {
634 if (s
->type
== CONST_SYM
&&
635 s
->value
.tag
== STRING_TAG
&&
636 !strcmp(s
->value
.val
.str
, value
)) {
644 ** find a symbol in the symbol table
646 Symbol
*LookupSymbol(const char *name
)
650 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
)
651 if (strcmp(s
->name
, name
) == 0)
653 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
654 if (strcmp(s
->name
, name
) == 0)
660 ** install s in symbol table
662 Symbol
*InstallSymbol(const char *name
, int type
, DataValue value
)
666 s
= (Symbol
*)malloc(sizeof(Symbol
));
667 s
->name
= (char *)malloc(strlen(name
)+1); /* +1 for '\0' */
668 strcpy(s
->name
, name
);
671 if (type
== LOCAL_SYM
) {
672 s
->next
= LocalSymList
;
675 s
->next
= GlobalSymList
;
682 ** Promote a symbol from local to global, removing it from the local symbol
685 Symbol
*PromoteToGlobal(Symbol
*sym
)
688 static DataValue noValue
= {NO_TAG
, {0}};
690 if (sym
->type
!= LOCAL_SYM
)
693 /* Remove sym from the local symbol list */
694 if (sym
== LocalSymList
)
695 LocalSymList
= sym
->next
;
697 for (s
= LocalSymList
; s
!= NULL
; s
= s
->next
) {
698 if (s
->next
== sym
) {
705 s
= LookupSymbol(sym
->name
);
708 return InstallSymbol(sym
->name
, GLOBAL_SYM
, noValue
);
712 ** Allocate memory for a string, and keep track of it, such that it
713 ** can be recovered later using GarbageCollectStrings. (A linked list
714 ** of pointers is maintained by threading through the memory behind
715 ** the returned pointers). Length does not include the terminating null
716 ** character, so to allocate space for a string of strlen == n, you must
717 ** use AllocString(n+1).
720 /*#define TRACK_GARBAGE_LEAKS*/
721 #ifdef TRACK_GARBAGE_LEAKS
722 static int numAllocatedStrings
= 0;
723 static int numAllocatedSparseArrayElements
= 0;
726 char *AllocString(int length
)
730 mem
= XtMalloc(length
+ sizeof(char *) + 1);
731 *((char **)mem
) = AllocatedStrings
;
732 AllocatedStrings
= mem
;
733 #ifdef TRACK_GARBAGE_LEAKS
734 ++numAllocatedStrings
;
736 return mem
+ sizeof(char *) + 1;
739 static SparseArrayEntry
*allocateSparseArrayEntry(void)
741 SparseArrayEntryWrapper
*mem
;
743 mem
= (SparseArrayEntryWrapper
*)XtMalloc(sizeof(SparseArrayEntryWrapper
));
744 mem
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
745 AllocatedSparseArrayEntries
= mem
;
746 #ifdef TRACK_GARBAGE_LEAKS
747 ++numAllocatedSparseArrayElements
;
749 return(&(mem
->data
));
752 static void MarkArrayContentsAsUsed(SparseArrayEntry
*arrayPtr
)
754 SparseArrayEntry
*globalSEUse
;
757 ((SparseArrayEntryWrapper
*)arrayPtr
)->inUse
= 1;
758 for (globalSEUse
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)arrayPtr
);
760 globalSEUse
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)globalSEUse
)) {
762 ((SparseArrayEntryWrapper
*)globalSEUse
)->inUse
= 1;
763 *(globalSEUse
->key
- 1) = 1;
764 if (globalSEUse
->value
.tag
== STRING_TAG
) {
765 *(globalSEUse
->value
.val
.str
- 1) = 1;
767 else if (globalSEUse
->value
.tag
== ARRAY_TAG
) {
768 MarkArrayContentsAsUsed((SparseArrayEntry
*)globalSEUse
->value
.val
.arrayPtr
);
775 ** Collect strings that are no longer referenced from the global symbol
776 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must
777 ** only be run after all macro activity has ceased.
780 void GarbageCollectStrings(void)
782 SparseArrayEntryWrapper
*nextAP
, *thisAP
;
786 /* mark all strings as unreferenced */
787 for (p
= AllocatedStrings
; p
!= NULL
; p
= *((char **)p
))
788 *(p
+ sizeof(char *)) = 0;
790 for (thisAP
= AllocatedSparseArrayEntries
;
791 thisAP
!= NULL
; thisAP
= (SparseArrayEntryWrapper
*)thisAP
->next
) {
795 /* Sweep the global symbol list, marking which strings are still
797 for (s
= GlobalSymList
; s
!= NULL
; s
= s
->next
)
798 if (s
->value
.tag
== STRING_TAG
)
799 *(s
->value
.val
.str
- 1) = 1;
800 else if (s
->value
.tag
== ARRAY_TAG
) {
801 MarkArrayContentsAsUsed((SparseArrayEntry
*)s
->value
.val
.arrayPtr
);
804 /* Collect all of the strings which remain unreferenced */
805 next
= AllocatedStrings
;
806 AllocatedStrings
= NULL
;
807 while (next
!= NULL
) {
809 next
= *((char **)p
);
810 if (*(p
+ sizeof(char *)) != 0) {
811 *((char **)p
) = AllocatedStrings
;
812 AllocatedStrings
= p
;
814 #ifdef TRACK_GARBAGE_LEAKS
815 --numAllocatedStrings
;
821 nextAP
= AllocatedSparseArrayEntries
;
822 AllocatedSparseArrayEntries
= NULL
;
823 while (nextAP
!= NULL
) {
825 nextAP
= (SparseArrayEntryWrapper
*)nextAP
->next
;
826 if (thisAP
->inUse
!= 0) {
827 thisAP
->next
= (struct SparseArrayEntryWrapper
*)AllocatedSparseArrayEntries
;
828 AllocatedSparseArrayEntries
= thisAP
;
831 #ifdef TRACK_GARBAGE_LEAKS
832 --numAllocatedSparseArrayElements
;
834 XtFree((void *)thisAP
);
838 #ifdef TRACK_GARBAGE_LEAKS
839 printf("str count = %d\nary count %d\n", numAllocatedStrings
, numAllocatedSparseArrayElements
);
844 ** Save and restore execution context to data structure "context"
846 static void saveContext(RestartData
*context
)
848 context
->stack
= Stack
;
849 context
->stackP
= StackP
;
850 context
->frameP
= FrameP
;
852 context
->runWindow
= InitiatingWindow
;
853 context
->focusWindow
= FocusWindow
;
855 static void restoreContext(RestartData
*context
)
857 Stack
= context
->stack
;
858 StackP
= context
->stackP
;
859 FrameP
= context
->frameP
;
861 InitiatingWindow
= context
->runWindow
;
862 FocusWindow
= context
->focusWindow
;
865 static void freeSymbolTable(Symbol
*symTab
)
869 while(symTab
!= NULL
) {
877 #define POP(dataVal) \
878 if (StackP == Stack) \
879 return execError(StackUnderflowMsg, ""); \
882 #define PUSH(dataVal) \
883 if (StackP >= &Stack[STACK_SIZE]) \
884 return execError(StackOverflowMsg, ""); \
887 #define PEEK(dataVal, peekIndex) \
888 dataVal = *(StackP - peekIndex - 1);
890 #define POP_INT(number) \
891 if (StackP == Stack) \
892 return execError(StackUnderflowMsg, ""); \
894 if (StackP->tag == STRING_TAG) { \
895 if (!stringToNum(StackP->val.str, &number)) \
896 return execError(StringToNumberMsg, ""); \
897 } else if (StackP->tag == INT_TAG) \
898 number = StackP->val.n; \
900 return(execError("can't convert array to integer", NULL));
902 #define POP_STRING(string) \
903 if (StackP == Stack) \
904 return execError(StackUnderflowMsg, ""); \
906 if (StackP->tag == INT_TAG) { \
907 string = AllocString(21); \
908 sprintf(string, "%d", StackP->val.n); \
909 } else if (StackP->tag == STRING_TAG) \
910 string = StackP->val.str; \
912 return(execError("can't convert array to string", NULL));
914 #define PEEK_STRING(string, peekIndex) \
915 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
916 string = AllocString(21); \
917 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \
919 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
920 string = (StackP - peekIndex - 1)->val.str; \
923 return(execError("can't convert array to string", NULL)); \
926 #define PEEK_INT(number, peekIndex) \
927 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \
928 if (!stringToNum((StackP - peekIndex - 1)->val.str, &number)) { \
929 return execError(StringToNumberMsg, ""); \
931 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \
932 number = (StackP - peekIndex - 1)->val.n; \
935 return(execError("can't convert array to string", NULL)); \
938 #define PUSH_INT(number) \
939 if (StackP >= &Stack[STACK_SIZE]) \
940 return execError(StackOverflowMsg, ""); \
941 StackP->tag = INT_TAG; \
942 StackP->val.n = number; \
945 #define PUSH_STRING(string) \
946 if (StackP >= &Stack[STACK_SIZE]) \
947 return execError(StackOverflowMsg, ""); \
948 StackP->tag = STRING_TAG; \
949 StackP->val.str = string; \
952 #define BINARY_NUMERIC_OPERATION(operator) \
956 PUSH_INT(n1 operator n2) \
959 #define UNARY_NUMERIC_OPERATION(operator) \
962 PUSH_INT(operator n) \
965 static int pushSymVal(void)
971 if (s
->type
== LOCAL_SYM
) {
972 *StackP
= *(FrameP
+ s
->value
.val
.n
);
973 } else if (s
->type
== GLOBAL_SYM
|| s
->type
== CONST_SYM
) {
975 } else if (s
->type
== ARG_SYM
) {
976 nArgs
= (FrameP
-1)->val
.n
;
977 argNum
= s
->value
.val
.n
;
979 return execError("referenced undefined argument: %s", s
->name
);
980 if (argNum
== N_ARGS_ARG_SYM
) {
981 StackP
->tag
= INT_TAG
;
982 StackP
->val
.n
= nArgs
;
984 *StackP
= *(FrameP
+ argNum
- nArgs
- 3);
985 } else if (s
->type
== PROC_VALUE_SYM
) {
988 if (!(s
->value
.val
.subr
)(FocusWindow
, NULL
, 0,
990 return execError(errMsg
, s
->name
);
993 return execError("reading non-variable: %s", s
->name
);
994 if (StackP
->tag
== NO_TAG
)
995 return execError("variable not set: %s", s
->name
);
997 if (StackP
>= &Stack
[STACK_SIZE
])
998 return execError(StackOverflowMsg
, "");
1002 static int assign(void) /* assign top value to next symbol */
1007 sym
= (Symbol
*)(*PC
++);
1008 if (sym
->type
!= GLOBAL_SYM
&& sym
->type
!= LOCAL_SYM
) {
1009 if (sym
->type
== ARG_SYM
)
1010 return execError("assignment to function argument: %s", sym
->name
);
1011 else if (sym
->type
== PROC_VALUE_SYM
)
1012 return execError("assignment to read-only variable: %s", sym
->name
);
1014 return execError("assignment to non-variable: %s", sym
->name
);
1016 if (StackP
== Stack
)
1017 return execError(StackUnderflowMsg
, "");
1019 if (sym
->type
== LOCAL_SYM
)
1020 dataPtr
= (FrameP
+ sym
->value
.val
.n
);
1022 dataPtr
= &sym
->value
;
1023 if (StackP
->tag
== ARRAY_TAG
) {
1024 ArrayCopy(dataPtr
, StackP
);
1032 static int dupStack(void)
1034 if (StackP
>= &Stack
[STACK_SIZE
])
1035 return execError(StackOverflowMsg
, "");
1036 *StackP
= *(StackP
- 1);
1042 ** if left and right arguments are arrays, then the result is a new array
1043 ** in which all the keys from both the right and left are copied
1044 ** the values from the right array are used in the result array when the
1045 ** keys are the same
1047 static int add(void)
1049 DataValue leftVal
, rightVal
, resultArray
;
1053 if (rightVal
.tag
== ARRAY_TAG
) {
1055 if (leftVal
.tag
== ARRAY_TAG
) {
1056 SparseArrayEntry
*leftIter
, *rightIter
;
1057 resultArray
.tag
= ARRAY_TAG
;
1058 resultArray
.val
.arrayPtr
= ArrayNew();
1062 leftIter
= arrayIterateFirst(&leftVal
);
1063 rightIter
= arrayIterateFirst(&rightVal
);
1064 while (leftIter
|| rightIter
) {
1065 int insertResult
= 1;
1067 if (leftIter
&& rightIter
) {
1068 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1069 if (compareResult
< 0) {
1070 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1071 leftIter
= arrayIterateNext(leftIter
);
1073 else if (compareResult
> 0) {
1074 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1075 rightIter
= arrayIterateNext(rightIter
);
1078 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1079 leftIter
= arrayIterateNext(leftIter
);
1080 rightIter
= arrayIterateNext(rightIter
);
1083 else if (leftIter
) {
1084 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1085 leftIter
= arrayIterateNext(leftIter
);
1088 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1089 rightIter
= arrayIterateNext(rightIter
);
1091 if (!insertResult
) {
1092 return(execError("array insertion failure", NULL
));
1098 return(execError("can't mix math with arrays and non-arrays", NULL
));
1110 ** if left and right arguments are arrays, then the result is a new array
1111 ** in which only the keys which exist in the left array but not in the right
1114 static int subtract(void)
1116 DataValue leftVal
, rightVal
, resultArray
;
1120 if (rightVal
.tag
== ARRAY_TAG
) {
1122 if (leftVal
.tag
== ARRAY_TAG
) {
1123 SparseArrayEntry
*leftIter
, *rightIter
;
1124 resultArray
.tag
= ARRAY_TAG
;
1125 resultArray
.val
.arrayPtr
= ArrayNew();
1129 leftIter
= arrayIterateFirst(&leftVal
);
1130 rightIter
= arrayIterateFirst(&rightVal
);
1132 int insertResult
= 1;
1134 if (leftIter
&& rightIter
) {
1135 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1136 if (compareResult
< 0) {
1137 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1138 leftIter
= arrayIterateNext(leftIter
);
1140 else if (compareResult
> 0) {
1141 rightIter
= arrayIterateNext(rightIter
);
1144 leftIter
= arrayIterateNext(leftIter
);
1145 rightIter
= arrayIterateNext(rightIter
);
1148 else if (leftIter
) {
1149 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1150 leftIter
= arrayIterateNext(leftIter
);
1152 if (!insertResult
) {
1153 return(execError("array insertion failure", NULL
));
1159 return(execError("can't mix math with arrays and non-arrays", NULL
));
1170 static int multiply(void)
1172 BINARY_NUMERIC_OPERATION(*)
1175 static int divide(void)
1181 return execError("division by zero", "");
1186 static int modulo(void)
1188 BINARY_NUMERIC_OPERATION(%)
1191 static int negate(void)
1193 UNARY_NUMERIC_OPERATION(-)
1196 static int increment(void)
1198 UNARY_NUMERIC_OPERATION(++)
1201 static int decrement(void)
1203 UNARY_NUMERIC_OPERATION(--)
1208 BINARY_NUMERIC_OPERATION(>)
1213 BINARY_NUMERIC_OPERATION(<)
1218 BINARY_NUMERIC_OPERATION(>=)
1223 BINARY_NUMERIC_OPERATION(<=)
1227 ** verify that compares are between integers and/or strings only
1235 if (v1
.tag
== INT_TAG
&& v2
.tag
== INT_TAG
) {
1236 v1
.val
.n
= v1
.val
.n
== v2
.val
.n
;
1238 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== STRING_TAG
) {
1239 v1
.val
.n
= !strcmp(v1
.val
.str
, v2
.val
.str
);
1241 else if (v1
.tag
== STRING_TAG
&& v2
.tag
== INT_TAG
) {
1243 if (!stringToNum(v1
.val
.str
, &number
)) {
1247 v1
.val
.n
= number
== v2
.val
.n
;
1250 else if (v2
.tag
== STRING_TAG
&& v1
.tag
== INT_TAG
) {
1252 if (!stringToNum(v2
.val
.str
, &number
)) {
1256 v1
.val
.n
= number
== v1
.val
.n
;
1260 return(execError("incompatible types to compare", NULL
));
1274 ** if left and right arguments are arrays, then the result is a new array
1275 ** in which only the keys which exist in both the right or left are copied
1276 ** the values from the right array are used in the result array
1278 static int bitAnd(void)
1280 DataValue leftVal
, rightVal
, resultArray
;
1284 if (rightVal
.tag
== ARRAY_TAG
) {
1286 if (leftVal
.tag
== ARRAY_TAG
) {
1287 SparseArrayEntry
*leftIter
, *rightIter
;
1288 resultArray
.tag
= ARRAY_TAG
;
1289 resultArray
.val
.arrayPtr
= ArrayNew();
1293 leftIter
= arrayIterateFirst(&leftVal
);
1294 rightIter
= arrayIterateFirst(&rightVal
);
1295 while (leftIter
&& rightIter
) {
1296 int insertResult
= 1;
1297 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1299 if (compareResult
< 0) {
1300 leftIter
= arrayIterateNext(leftIter
);
1302 else if (compareResult
> 0) {
1303 rightIter
= arrayIterateNext(rightIter
);
1306 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1307 leftIter
= arrayIterateNext(leftIter
);
1308 rightIter
= arrayIterateNext(rightIter
);
1310 if (!insertResult
) {
1311 return(execError("array insertion failure", NULL
));
1317 return(execError("can't mix math with arrays and non-arrays", NULL
));
1329 ** if left and right arguments are arrays, then the result is a new array
1330 ** in which only the keys which exist in either the right or left but not both
1333 static int bitOr(void)
1335 DataValue leftVal
, rightVal
, resultArray
;
1339 if (rightVal
.tag
== ARRAY_TAG
) {
1341 if (leftVal
.tag
== ARRAY_TAG
) {
1342 SparseArrayEntry
*leftIter
, *rightIter
;
1343 resultArray
.tag
= ARRAY_TAG
;
1344 resultArray
.val
.arrayPtr
= ArrayNew();
1348 leftIter
= arrayIterateFirst(&leftVal
);
1349 rightIter
= arrayIterateFirst(&rightVal
);
1350 while (leftIter
|| rightIter
) {
1351 int insertResult
= 1;
1353 if (leftIter
&& rightIter
) {
1354 int compareResult
= arrayEntryCompare((rbTreeNode
*)leftIter
, (rbTreeNode
*)rightIter
);
1355 if (compareResult
< 0) {
1356 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1357 leftIter
= arrayIterateNext(leftIter
);
1359 else if (compareResult
> 0) {
1360 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1361 rightIter
= arrayIterateNext(rightIter
);
1364 leftIter
= arrayIterateNext(leftIter
);
1365 rightIter
= arrayIterateNext(rightIter
);
1368 else if (leftIter
) {
1369 insertResult
= ArrayInsert(&resultArray
, leftIter
->key
, &leftIter
->value
);
1370 leftIter
= arrayIterateNext(leftIter
);
1373 insertResult
= ArrayInsert(&resultArray
, rightIter
->key
, &rightIter
->value
);
1374 rightIter
= arrayIterateNext(rightIter
);
1376 if (!insertResult
) {
1377 return(execError("array insertion failure", NULL
));
1383 return(execError("can't mix math with arrays and non-arrays", NULL
));
1394 static int and(void)
1396 BINARY_NUMERIC_OPERATION(&&)
1401 BINARY_NUMERIC_OPERATION(||)
1404 static int not(void)
1406 UNARY_NUMERIC_OPERATION(!)
1409 static int power(void)
1414 /* We need to round to deal with pow() giving results slightly above
1415 or below the real result since it deals with floating point numbers.
1416 Note: We're not really wanting rounded results, we merely
1417 want to deal with this simple issue. So, 2^-2 = .5, but we
1418 don't want to round this to 1. This is mainly intended to deal with
1419 4^2 = 15.999996 and 16.000001.
1421 if (n2
< 0 && n1
!= 1 && n1
!= -1) {
1423 /* since we're integer only, nearly all negative exponents result in 0 */
1427 /* allow error to occur */
1428 n3
= (int)pow((double)n1
, (double)n2
);
1432 if ((n1
< 0) && (n2
& 1)) {
1433 /* round to nearest integer for negative values*/
1434 n3
= (int)(pow((double)n1
, (double)n2
) - (double)0.5);
1437 /* round to nearest integer for positive values*/
1438 n3
= (int)(pow((double)n1
, (double)n2
) + (double)0.5);
1442 return errCheck("exponentiation");
1445 static int concat(void)
1447 char *s1
, *s2
, *out
;
1453 out
= AllocString(len1
+ len2
+ 1);
1454 strncpy(out
, s1
, len1
);
1455 strcpy(&out
[len1
], s2
);
1461 ** Call a subroutine or function (user defined or built-in). Args are the
1462 ** subroutine's symbol, and the number of arguments which have been pushed
1465 ** For a macro subroutine, the return address, frame pointer, number of
1466 ** arguments and space for local variables are added to the stack, and the
1467 ** PC is set to point to the new function. For a built-in routine, the
1468 ** arguments are popped off the stack, and the routine is just called.
1471 ** The call stack for a subroutine call looks like
1473 ** SP after return -> arg1
1479 ** SP before call -> ReturnAddress
1490 static int callSubroutine(void)
1494 static DataValue noValue
= {NO_TAG
, {0}};
1498 sym
= (Symbol
*)*PC
++;
1501 if (nArgs
> MAX_ARGS
)
1502 return execError("too many arguments to subroutine %s (max 9)",
1506 ** If the subroutine is built-in, call the built-in routine
1508 if (sym
->type
== C_FUNCTION_SYM
) {
1509 DataValue result
, argList
[MAX_ARGS
];
1511 /* pop arguments off the stack and put them in the argument list */
1512 for (i
=nArgs
-1; i
>=0; i
--) {
1516 /* Call the function and check for preemption */
1517 PreemptRequest
= False
;
1518 if (!(sym
->value
.val
.subr
)(FocusWindow
, argList
,
1519 nArgs
, &result
, &errMsg
))
1520 return execError(errMsg
, sym
->name
);
1521 if (*PC
== fetchRetVal
) {
1522 if (result
.tag
== NO_TAG
)
1523 return execError("%s does not return a value", sym
->name
);
1527 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1531 ** Call a macro subroutine:
1533 ** Push all of the required information to resume, and make space on the
1534 ** stack for local variables (and initialize them), on top of the argument
1535 ** values which are already there.
1537 if (sym
->type
== MACRO_FUNCTION_SYM
) {
1538 StackP
->tag
= NO_TAG
;
1539 StackP
->val
.inst
= PC
;
1541 StackP
->tag
= NO_TAG
;
1542 StackP
->val
.dataval
= FrameP
;
1544 StackP
->tag
= NO_TAG
;
1545 StackP
->val
.n
= nArgs
;
1548 prog
= (Program
*)sym
->value
.val
.str
;
1550 for (s
= prog
->localSymList
; s
!= NULL
; s
= s
->next
) {
1551 *(FrameP
+ s
->value
.val
.n
) = noValue
;
1558 ** Call an action routine
1560 if (sym
->type
== ACTION_ROUTINE_SYM
) {
1561 String argList
[MAX_ARGS
];
1562 Cardinal numArgs
= nArgs
;
1563 XKeyEvent key_event
;
1567 /* Create a fake event with a timestamp suitable for actions which need
1568 timestamps, a marker to indicate that the call was from a macro
1569 (to stop shell commands from putting up their own separate banner) */
1570 disp
=XtDisplay(InitiatingWindow
->shell
);
1571 win
=XtWindow(InitiatingWindow
->shell
);
1573 key_event
.type
= KeyPress
;
1574 key_event
.send_event
= MACRO_EVENT_MARKER
;
1575 key_event
.time
=XtLastTimestampProcessed(XtDisplay(InitiatingWindow
->shell
));
1577 /* The following entries are just filled in to avoid problems
1578 in strange cases, like calling "self_insert()" directly from the
1579 macro menu. In fact the display was sufficient to cure this crash. */
1580 key_event
.display
=disp
;
1581 key_event
.window
=key_event
.root
=key_event
.subwindow
=win
;
1583 /* pop arguments off the stack and put them in the argument list */
1584 for (i
=nArgs
-1; i
>=0; i
--) {
1585 POP_STRING(argList
[i
])
1588 /* Call the action routine and check for preemption */
1589 PreemptRequest
= False
;
1590 (sym
->value
.val
.xtproc
)(FocusWindow
->lastFocus
,
1591 (XEvent
*)&key_event
, argList
, &numArgs
);
1592 if (*PC
== fetchRetVal
)
1593 return execError("%s does not return a value", sym
->name
);
1594 return PreemptRequest
? STAT_PREEMPT
: STAT_OK
;
1597 /* Calling a non subroutine symbol */
1598 return execError("%s is not a function or subroutine", sym
->name
);
1602 ** This should never be executed, returnVal checks for the presence of this
1603 ** instruction at the PC to decide whether to push the function's return
1604 ** value, then skips over it without executing.
1606 static int fetchRetVal(void)
1608 return execError("internal error: frv", NULL
);
1611 static int returnNoVal(void)
1613 return returnValOrNone(False
);
1615 static int returnVal(void)
1617 return returnValOrNone(True
);
1621 ** Return from a subroutine call
1623 static int returnValOrNone(int valOnStack
)
1626 static DataValue noValue
= {NO_TAG
, {0}};
1629 /* return value is on the stack */
1634 /* pop past local variables */
1637 /* get stored return information */
1638 nArgs
= (--StackP
)->val
.n
;
1639 FrameP
= (--StackP
)->val
.dataval
;
1640 PC
= (--StackP
)->val
.inst
;
1642 /* pop past function arguments */
1645 /* push returned value, if requsted */
1652 } else if (*PC
== fetchRetVal
) {
1658 "using return value of %s which does not return a value",
1659 ((Symbol
*)*(PC
- 2))->name
);
1663 /* NULL return PC indicates end of program */
1664 return PC
== NULL
? STAT_DONE
: STAT_OK
;
1668 ** Unconditional branch offset by immediate operand
1670 static int branch(void)
1677 ** Conditional branches if stack value is True/False (non-zero/0) to address
1678 ** of immediate operand (pops stack)
1680 static int branchTrue(void)
1686 addr
= PC
+ (int)*PC
;
1693 static int branchFalse(void)
1699 addr
= PC
+ (int)*PC
;
1708 ** Ignore the address following the instruction and continue. Why? So
1709 ** some code that uses conditional branching doesn't have to figure out
1710 ** whether to store a branch address.
1712 static int branchNever(void)
1719 ** recursively copy(duplicate) the sparse array nodes of an array
1720 ** this does not duplicate the key/node data since they are never
1721 ** modified, only replaced
1723 int ArrayCopy(DataValue
*dstArray
, DataValue
*srcArray
)
1725 SparseArrayEntry
*srcIter
;
1727 dstArray
->tag
= ARRAY_TAG
;
1728 dstArray
->val
.arrayPtr
= ArrayNew();
1730 srcIter
= arrayIterateFirst(srcArray
);
1732 if (srcIter
->value
.tag
== ARRAY_TAG
) {
1736 errNum
= ArrayCopy(&tmpArray
, &srcIter
->value
);
1737 if (errNum
!= STAT_OK
) {
1740 if (!ArrayInsert(dstArray
, srcIter
->key
, &tmpArray
)) {
1741 return(execError("array copy failed", NULL
));
1745 if (!ArrayInsert(dstArray
, srcIter
->key
, &srcIter
->value
)) {
1746 return(execError("array copy failed", NULL
));
1749 srcIter
= arrayIterateNext(srcIter
);
1755 ** creates an allocated string of a single key for all the sub-scripts
1756 ** using ARRAY_DIM_SEP as a separator
1757 ** this function uses the PEEK macros in order to remove most limits on
1758 ** the number of arguments to an array
1759 ** I really need to optimize the size approximation rather than assuming
1760 ** a worst case size for every integer argument
1762 static int makeArrayKeyFromArgs(int nArgs
, char **keyString
)
1765 int maxIntDigits
= (sizeof(tmpVal
.val
.n
) * 3) + 1;
1766 int sepLen
= strlen(ARRAY_DIM_SEP
);
1770 keyLength
= sepLen
* (nArgs
- 1);
1771 for (i
= nArgs
- 1; i
>= 0; --i
) {
1773 if (tmpVal
.tag
== INT_TAG
) {
1774 keyLength
+= maxIntDigits
;
1776 else if (tmpVal
.tag
== STRING_TAG
) {
1777 keyLength
+= strlen(tmpVal
.val
.str
);
1780 return(execError("can only index array with string or int.", NULL
));
1783 *keyString
= AllocString(keyLength
+ 1);
1784 (*keyString
)[0] = 0;
1785 for (i
= nArgs
- 1; i
>= 0; --i
) {
1786 if (i
!= nArgs
- 1) {
1787 strcat(*keyString
, ARRAY_DIM_SEP
);
1790 if (tmpVal
.tag
== INT_TAG
) {
1791 sprintf(&((*keyString
)[strlen(*keyString
)]), "%d", tmpVal
.val
.n
);
1793 else if (tmpVal
.tag
== STRING_TAG
) {
1794 strcat(*keyString
, tmpVal
.val
.str
);
1797 return(execError("can only index array with string or int.", NULL
));
1800 for (i
= nArgs
- 1; i
>= 0; --i
) {
1807 ** allocate an empty array node, this is used as the root node and never
1808 ** contains any data, only refernces to other nodes
1810 static rbTreeNode
*arrayEmptyAllocator(void)
1812 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
1814 newNode
->key
= NULL
;
1815 newNode
->value
.tag
= NO_TAG
;
1817 return((rbTreeNode
*)newNode
);
1821 ** create and copy array node and copy contents, we merely copy pointers
1822 ** since they are never modified, only replaced
1824 static rbTreeNode
*arrayAllocateNode(rbTreeNode
*src
)
1826 SparseArrayEntry
*newNode
= allocateSparseArrayEntry();
1828 newNode
->key
= ((SparseArrayEntry
*)src
)->key
;
1829 newNode
->value
= ((SparseArrayEntry
*)src
)->value
;
1831 return((rbTreeNode
*)newNode
);
1835 ** copy array node data, we merely copy pointers since they are never
1836 ** modified, only replaced
1838 static int arrayEntryCopyToNode(rbTreeNode
*dst
, rbTreeNode
*src
)
1840 ((SparseArrayEntry
*)dst
)->key
= ((SparseArrayEntry
*)src
)->key
;
1841 ((SparseArrayEntry
*)dst
)->value
= ((SparseArrayEntry
*)src
)->value
;
1846 ** compare two array nodes returning an integer value similar to strcmp()
1848 static int arrayEntryCompare(rbTreeNode
*left
, rbTreeNode
*right
)
1850 return(strcmp(((SparseArrayEntry
*)left
)->key
, ((SparseArrayEntry
*)right
)->key
));
1854 ** dispose an array node, garbage collection handles this, so we mark it
1855 ** to allow iterators in macro language to determine they have been unlinked
1857 static void arrayDisposeNode(rbTreeNode
*src
)
1859 /* Let garbage collection handle this but mark it so iterators can tell */
1866 struct SparseArrayEntry
*ArrayNew(void)
1868 return((struct SparseArrayEntry
*)rbTreeNew(arrayEmptyAllocator
));
1872 ** insert a DataValue into an array, allocate the array if needed
1873 ** keyStr must be a string that was allocated with AllocString()
1875 int ArrayInsert(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
1877 SparseArrayEntry tmpEntry
;
1878 rbTreeNode
*insertedNode
;
1880 tmpEntry
.key
= keyStr
;
1881 tmpEntry
.value
= *theValue
;
1883 if (theArray
->val
.arrayPtr
== NULL
) {
1884 theArray
->val
.arrayPtr
= ArrayNew();
1886 if (theArray
->val
.arrayPtr
!= NULL
) {
1887 insertedNode
= rbTreeInsert((rbTreeNode
*)(theArray
->val
.arrayPtr
),
1888 (rbTreeNode
*)&tmpEntry
,
1889 arrayEntryCompare
, arrayAllocateNode
, arrayEntryCopyToNode
);
1901 ** remove a node from an array whose key matches keyStr
1903 void ArrayDelete(DataValue
*theArray
, char *keyStr
)
1905 SparseArrayEntry searchEntry
;
1907 if (theArray
->val
.arrayPtr
) {
1908 searchEntry
.key
= keyStr
;
1909 rbTreeDelete((rbTreeNode
*)theArray
->val
.arrayPtr
, (rbTreeNode
*)&searchEntry
,
1910 arrayEntryCompare
, arrayDisposeNode
);
1915 ** remove all nodes from an array
1917 void ArrayDeleteAll(DataValue
*theArray
)
1920 if (theArray
->val
.arrayPtr
) {
1921 rbTreeNode
*iter
= rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
1923 rbTreeNode
*nextIter
= rbTreeNext(iter
);
1924 rbTreeDeleteNode((rbTreeNode
*)theArray
->val
.arrayPtr
,
1925 iter
, arrayDisposeNode
);
1933 ** returns the number of elements (nodes containing values) of an array
1935 int ArraySize(DataValue
*theArray
)
1937 if (theArray
->val
.arrayPtr
) {
1938 return(rbTreeSize((rbTreeNode
*)theArray
->val
.arrayPtr
));
1946 ** retrieves an array node whose key matches
1947 ** returns 1 for success 0 for not found
1949 int ArrayGet(DataValue
*theArray
, char *keyStr
, DataValue
*theValue
)
1951 SparseArrayEntry searchEntry
;
1952 rbTreeNode
*foundNode
;
1954 if (theArray
->val
.arrayPtr
) {
1955 searchEntry
.key
= keyStr
;
1956 foundNode
= rbTreeFind((rbTreeNode
*)theArray
->val
.arrayPtr
,
1957 (rbTreeNode
*)&searchEntry
, arrayEntryCompare
);
1959 *theValue
= ((SparseArrayEntry
*)foundNode
)->value
;
1967 ** get pointer to start iterating an array
1969 SparseArrayEntry
*arrayIterateFirst(DataValue
*theArray
)
1971 SparseArrayEntry
*startPos
;
1972 if (theArray
->val
.arrayPtr
) {
1973 startPos
= (SparseArrayEntry
*)rbTreeBegin((rbTreeNode
*)theArray
->val
.arrayPtr
);
1982 ** move iterator to next entry in array
1984 SparseArrayEntry
*arrayIterateNext(SparseArrayEntry
*iterator
)
1986 SparseArrayEntry
*nextPos
;
1988 nextPos
= (SparseArrayEntry
*)rbTreeNext((rbTreeNode
*)iterator
);
1997 ** evaluate an array element and push the result onto the stack
1999 static int arrayRef(void)
2002 DataValue srcArray
, valueItem
;
2003 char *keyString
= NULL
;
2010 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
);
2011 if (errNum
!= STAT_OK
) {
2016 if (srcArray
.tag
== ARRAY_TAG
) {
2017 if (!ArrayGet(&srcArray
, keyString
, &valueItem
)) {
2018 return(execError("referenced array value not in array", NULL
));
2024 return(execError("operator [] on non-array", NULL
));
2029 if (srcArray
.tag
== ARRAY_TAG
) {
2030 PUSH_INT(ArraySize(&srcArray
))
2034 return(execError("operator [] on non-array", NULL
));
2040 ** assign to an array element, make the symbol an array if it isn't already
2042 static int arrayAssign(void)
2045 char *keyString
= NULL
;
2046 DataValue
*dstPtr
, srcValue
;
2050 sym
= (Symbol
*)*PC
;
2055 if (sym
->type
== LOCAL_SYM
) {
2056 dstPtr
= (FrameP
+ sym
->value
.val
.n
);
2058 else if (sym
->type
== GLOBAL_SYM
) {
2059 dstPtr
= &(sym
->value
);
2061 else if (sym
->type
== ARG_SYM
) {
2062 return(execError("assignment to function argument: %s", sym
->name
));
2065 return(execError("assignment to non-variable: %s", sym
->name
));
2071 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
);
2072 if (errNum
!= STAT_OK
) {
2076 if (dstPtr
->tag
!= ARRAY_TAG
) {
2077 dstPtr
->tag
= ARRAY_TAG
;
2078 dstPtr
->val
.arrayPtr
= ArrayNew();
2080 if (ArrayInsert(dstPtr
, keyString
, &srcValue
)) {
2084 return(execError("array member allocation failure", NULL
));
2087 return(execError("empty operator []", NULL
));
2091 ** setup symbol values for array iteration in interpreter
2093 static int beginArrayIter(void)
2097 DataValue
*iteratorValPtr
;
2098 DataValue
*arrayValPtr
;
2100 array
= (Symbol
*)*PC
;
2102 iterator
= (Symbol
*)*PC
;
2105 if (array
->type
== LOCAL_SYM
) {
2106 arrayValPtr
= (FrameP
+ array
->value
.val
.n
);
2108 else if (array
->type
== GLOBAL_SYM
) {
2109 arrayValPtr
= &(array
->value
);
2111 else if (array
->type
== ARG_SYM
) {
2112 int nArgs
= (FrameP
-1)->val
.n
;
2113 int argNum
= array
->value
.val
.n
;
2114 if (argNum
>= nArgs
) {
2115 return(execError("referenced undefined argument: %s", array
->name
));
2117 if (argNum
!= N_ARGS_ARG_SYM
) {
2118 arrayValPtr
= (FrameP
+ argNum
- nArgs
- 3);
2121 return(execError("can't iterate non-array", array
->name
));
2125 return(execError("can't iterate variable: %s", array
->name
));
2128 if (iterator
->type
== LOCAL_SYM
) {
2129 iteratorValPtr
= (FrameP
+ iterator
->value
.val
.n
);
2132 return(execError("bad temporary iterator: %s", iterator
->name
));
2134 iteratorValPtr
->tag
= INT_TAG
;
2136 if (arrayValPtr
->tag
!= ARRAY_TAG
) {
2137 return(execError("can't iterate non-array: %s", array
->name
));
2140 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateFirst(arrayValPtr
);
2145 ** copy key to symbol if node is still valid, marked bad by a color of -1
2146 ** then move iterator to next node
2147 ** this allows iterators to progress even if you delete any node in the array
2148 ** except the item just after the current key
2150 static int arrayIter(void)
2154 DataValue
*iteratorValPtr
;
2155 DataValue
*itemValPtr
;
2156 SparseArrayEntry
*thisEntry
;
2159 item
= (Symbol
*)*PC
;
2161 iterator
= (Symbol
*)*PC
;
2163 branchAddr
= PC
+ (int)*PC
;
2166 if (item
->type
== LOCAL_SYM
) {
2167 itemValPtr
= (FrameP
+ item
->value
.val
.n
);
2169 else if (item
->type
== GLOBAL_SYM
) {
2170 itemValPtr
= &(item
->value
);
2173 return(execError("can't assign to: %s", item
->name
));
2175 itemValPtr
->tag
= NO_TAG
;
2177 if (iterator
->type
== LOCAL_SYM
) {
2178 iteratorValPtr
= (FrameP
+ iterator
->value
.val
.n
);
2181 return(execError("bad temporary iterator: %s", iterator
->name
));
2184 thisEntry
= (SparseArrayEntry
*)iteratorValPtr
->val
.arrayPtr
;
2185 if (thisEntry
&& thisEntry
->nodePtrs
.color
!= -1) {
2186 itemValPtr
->tag
= STRING_TAG
;
2187 itemValPtr
->val
.str
= thisEntry
->key
;
2189 iteratorValPtr
->val
.arrayPtr
= (struct SparseArrayEntry
*)arrayIterateNext(thisEntry
);
2198 ** deletermine if a key or keys exists in an array
2199 ** if the left argument is a string or integer a single check is performed
2200 ** if the key exists, 1 is pushed onto the stack, otherwise 0
2201 ** if the left argument is an array 1 is pushed onto the stack if every key
2202 ** in the left array exists in the right array, otherwise 0
2204 static int inArray(void)
2206 DataValue theArray
, leftArray
, theValue
;
2211 if (theArray
.tag
!= ARRAY_TAG
) {
2212 return(execError("operator in on non-array", NULL
));
2215 if (leftArray
.tag
== ARRAY_TAG
) {
2216 SparseArrayEntry
*iter
;
2220 iter
= arrayIterateFirst(&leftArray
);
2221 while (inResult
&& iter
) {
2222 inResult
= inResult
&& ArrayGet(&theArray
, iter
->key
, &theValue
);
2223 iter
= arrayIterateNext(iter
);
2228 if (ArrayGet(&theArray
, keyStr
, &theValue
)) {
2237 ** remove a given key from an array unless nDim is 0, then all keys are removed
2239 static int deleteArrayElement(void)
2242 char *keyString
= NULL
;
2247 sym
= (Symbol
*)*PC
;
2252 if (sym
->type
== LOCAL_SYM
) {
2253 dstPtr
= (FrameP
+ sym
->value
.val
.n
);
2255 else if (sym
->type
== GLOBAL_SYM
) {
2256 dstPtr
= &(sym
->value
);
2258 else if (sym
->type
== ARG_SYM
) {
2259 return(execError("can't delete from function argument: %s", sym
->name
));
2262 return(execError("can't delete non-array element: %s", sym
->name
));
2265 if (dstPtr
->tag
!= ARRAY_TAG
) {
2266 return(execError("attempt to delete from non-array: %s", sym
->name
));
2269 errNum
= makeArrayKeyFromArgs(nDim
, &keyString
);
2270 if (errNum
!= STAT_OK
) {
2274 ArrayDelete(dstPtr
, keyString
);
2278 ArrayDeleteAll(dstPtr
);
2284 ** checks errno after operations which can set it. If an error occured,
2285 ** creates appropriate error messages and returns false
2287 static int errCheck(const char *s
)
2290 return execError("%s argument out of domain", s
);
2291 else if (errno
== ERANGE
)
2292 return execError("%s result out of range", s
);
2298 ** combine two strings in a static area and set ErrMsg to point to the
2299 ** result. Returns false so a single return execError() statement can
2300 ** be used to both process the message and return.
2302 static int execError(const char *s1
, const char *s2
)
2304 static char msg
[MAX_ERR_MSG_LEN
];
2306 sprintf(msg
, s1
, s2
);
2311 static int stringToNum(const char *string
, int *number
)
2316 /*... this is still not finished */
2317 for (c
=string
, i
=0; *c
!= '\0'; i
++, c
++)
2318 if (!(isdigit((unsigned char)*c
) || *c
!= ' ' || *c
!= '\t'))
2320 if (sscanf(string
, "%d", number
) != 1) {
2326 #ifdef DEBUG_ASSEMBLY /* For debugging code generation */
2327 static void disasm(Program
*prog
, int nInstr
)
2329 static const char *opNames
[N_OPS
] = {"returnNoVal", "returnVal", "pushSymVal",
2330 "dupStack", "add", "subtract", "multiply", "divide", "modulo",
2331 "negate", "increment", "decrement", "gt", "lt", "ge", "le", "eq",
2332 "ne", "bitAnd", "bitOr", "and", "or", "not", "power", "concat",
2333 "assign", "callSubroutine", "fetchRetVal", "branch", "branchTrue",
2334 "branchFalse", "branchNever", "arrayRef", "arrayAssign",
2335 "beginArrayIter", "arrayIter", "inArray", "deleteArrayElement"};
2338 for (i
=0; i
<nInstr
; i
++) {
2339 printf("%x ", &prog
->code
[i
]);
2340 for (j
=0; j
<N_OPS
; j
++) {
2341 if (prog
->code
[i
] == OpFns
[j
]) {
2342 printf("%s", opNames
[j
]);
2343 if (j
== OP_PUSH_SYM
|| j
== OP_ASSIGN
) {
2344 printf(" %s", ((Symbol
*)prog
->code
[i
+1])->name
);
2346 } else if (j
== OP_BRANCH
|| j
== OP_BRANCH_FALSE
||
2347 j
== OP_BRANCH_NEVER
) {
2348 printf(" (%d) %x", (int)prog
->code
[i
+1],
2349 &prog
->code
[i
+1] + (int)prog
->code
[i
+1]);
2351 } else if (j
== OP_SUBR_CALL
|| j
== OP_ARRAY_ASSIGN
) {
2352 printf(" %s (%d arg)", ((Symbol
*)prog
->code
[i
+1])->name
,
2355 } else if (j
== OP_BEGIN_ARRAY_ITER
) {
2357 ((Symbol
*)prog
->code
[i
+1])->name
,
2358 ((Symbol
*)prog
->code
[i
+2])->name
);
2360 } else if (j
== OP_ARRAY_ITER
) {
2361 printf(" %s in %s (%d) %x",
2362 ((Symbol
*)prog
->code
[i
+1])->name
,
2363 ((Symbol
*)prog
->code
[i
+2])->name
,
2364 (int)prog
->code
[i
+3],
2365 &prog
->code
[i
+3] + (int)prog
->code
[i
+3]);
2374 printf("%x\n", prog
->code
[i
]);
2377 #endif /* ifdef DEBUG_ASSEMBLY */