UNIXworkcode

1 /******************************************************************************* 2 * * 3 * interpret.c -- Nirvana Editor macro interpreter * 4 * * 5 * Copyright (C) 1999 Mark Edel * 6 * * 7 * This is free software; you can redistribute it and/or modify it under the * 8 * terms of the GNU General Public License as published by the Free Software * 9 * Foundation; either version 2 of the License, or (at your option) any later * 10 * version. In addition, you may distribute version of this program linked to * 11 * Motif or Open Motif. See README for details. * 12 * * 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 * 16 * for more details. * 17 * * 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 * 21 * * 22 * Nirvana Text Editor * 23 * April, 1997 * 24 * * 25 * Written by Mark Edel * 26 * * 27 *******************************************************************************/ 28 29 #ifdef HAVE_CONFIG_H 30 #include "../config.h" 31 #endif 32 33 #include "interpret.h" 34 #include "textBuf.h" 35 #include "nedit.h" 36 #include "menu.h" 37 #include "text.h" 38 #include "../util/rbTree.h" 39 #include "../util/nedit_malloc.h" 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <math.h> 45 #include <limits.h> 46 #include <ctype.h> 47 #include <errno.h> 48 49 #ifndef __MVS__ 50 #include <sys/param.h> 51 #endif 52 53 54 #include <X11/Intrinsic.h> 55 #include <Xm/Xm.h> 56 57 #include "window.h" 58 59 #ifdef HAVE_DEBUG_H 60 #include "../debug.h" 61 #endif 62 63 #define PROGRAM_SIZE 4096 /* Maximum program size */ 64 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */ 65 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts 66 allowed per program */ 67 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is 68 allowed to execute before preempting and 69 returning to allow other things to run */ 70 71 /* Temporary markers placed in a branch address location to designate 72 which loop address (break or continue) the location needs */ 73 #define NEEDS_BREAK 1 74 #define NEEDS_CONTINUE 2 75 76 #define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */ 77 78 enum opStatusCodes {STAT_OK=2, STAT_DONE, STAT_ERROR, STAT_PREEMPT}; 79 80 static void addLoopAddr(Inst *addr); 81 static void saveContext(RestartData *context); 82 static void restoreContext(RestartData *context); 83 static int returnNoVal(void); 84 static int returnVal(void); 85 static int returnValOrNone(int valOnStack); 86 static int pushSymVal(void); 87 static int pushArgVal(void); 88 static int pushArgCount(void); 89 static int pushArgArray(void); 90 static int pushArraySymVal(void); 91 static int dupStack(void); 92 static int add(void); 93 static int subtract(void); 94 static int multiply(void); 95 static int divide(void); 96 static int modulo(void); 97 static int negate(void); 98 static int increment(void); 99 static int decrement(void); 100 static int gt(void); 101 static int lt(void); 102 static int ge(void); 103 static int le(void); 104 static int eq(void); 105 static int ne(void); 106 static int bitAnd(void); 107 static int bitOr(void); 108 static int and(void); 109 static int or(void); 110 static int not(void); 111 static int power(void); 112 static int concat(void); 113 static int assign(void); 114 static int callSubroutine(void); 115 static int fetchRetVal(void); 116 static int branch(void); 117 static int branchTrue(void); 118 static int branchFalse(void); 119 static int branchNever(void); 120 static int arrayRef(void); 121 static int arrayAssign(void); 122 static int arrayRefAndAssignSetup(void); 123 static int beginArrayIter(void); 124 static int arrayIter(void); 125 static int inArray(void); 126 static int deleteArrayElement(void); 127 static void freeSymbolTable(Symbol *symTab); 128 static int errCheck(const char *s); 129 static int execError(const char *s1, const char *s2); 130 static rbTreeNode *arrayEmptyAllocator(void); 131 static rbTreeNode *arrayAllocateNode(rbTreeNode *src); 132 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src); 133 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right); 134 static void arrayDisposeNode(rbTreeNode *src); 135 static SparseArrayEntry *allocateSparseArrayEntry(void); 136 137 /*#define DEBUG_ASSEMBLY*/ 138 /*#define DEBUG_STACK*/ 139 140 #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) 141 #define DEBUG_DISASSEMBLER 142 static void disasm(Inst *inst, int nInstr); 143 #endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */ 144 145 #ifdef DEBUG_ASSEMBLY /* for disassembly */ 146 #define DISASM(i, n) disasm(i, n) 147 #else /* #ifndef DEBUG_ASSEMBLY */ 148 #define DISASM(i, n) 149 #endif /* #ifndef DEBUG_ASSEMBLY */ 150 151 #ifdef DEBUG_STACK /* for run-time instruction and stack trace */ 152 static void stackdump(int n, int extra); 153 #define STACKDUMP(n, x) stackdump(n, x) 154 #define DISASM_RT(i, n) disasm(i, n) 155 #else /* #ifndef DEBUG_STACK */ 156 #define STACKDUMP(n, x) 157 #define DISASM_RT(i, n) 158 #endif /* #ifndef DEBUG_STACK */ 159 160 /* Global symbols and function definitions */ 161 static Symbol *GlobalSymList = NULL; 162 163 /* List of all memory allocated for strings */ 164 static char *AllocatedStrings = NULL; 165 166 typedef struct SparseArrayEntryWrapperTag { 167 SparseArrayEntry data; /* LEAVE this as top entry */ 168 int inUse; /* we use pointers to the data to refer to the entire struct */ 169 struct SparseArrayEntryWrapperTag *next; 170 } SparseArrayEntryWrapper; 171 172 static SparseArrayEntryWrapper *AllocatedSparseArrayEntries = NULL; 173 174 /* Message strings used in macros (so they don't get repeated every time 175 the macros are used */ 176 static const char *StackOverflowMsg = "macro stack overflow"; 177 static const char *StackUnderflowMsg = "macro stack underflow"; 178 static const char *StringToNumberMsg = "string could not be converted to number"; 179 180 /* Temporary global data for use while accumulating programs */ 181 static Symbol *LocalSymList = NULL; /* symbols local to the program */ 182 static Inst Prog[PROGRAM_SIZE]; /* the program */ 183 static Inst *ProgP; /* next free spot for code gen. */ 184 static Inst *LoopStack[LOOP_STACK_SIZE]; /* addresses of break, cont stmts */ 185 static Inst **LoopStackPtr = LoopStack; /* to fill at the end of a loop */ 186 187 /* Global data for the interpreter */ 188 static DataValue *TheStack; /* the stack */ 189 static DataValue *StackP; /* next free spot on stack */ 190 static DataValue *FrameP; /* frame pointer (start of local variables 191 for the current subroutine invocation) */ 192 static Inst *PC; /* program counter during execution */ 193 static char *ErrMsg; /* global for returning error messages 194 from executing functions */ 195 static WindowInfo 196 *InitiatingWindow = NULL; /* window from which macro was run */ 197 static WindowInfo *FocusWindow; /* window on which macro commands operate */ 198 static int PreemptRequest; /* passes preemption requests from called 199 routines back up to the interpreter */ 200 201 /* Array for mapping operations to functions for performing the operations 202 Must correspond to the enum called "operations" in interpret.h */ 203 static int (*OpFns[N_OPS])() = {returnNoVal, returnVal, pushSymVal, dupStack, 204 add, subtract, multiply, divide, modulo, negate, increment, decrement, 205 gt, lt, ge, le, eq, ne, bitAnd, bitOr, and, or, not, power, concat, 206 assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse, 207 branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray, 208 deleteArrayElement, pushArraySymVal, 209 arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray}; 210 211 /* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */ 212 #define FP_ARG_ARRAY_CACHE_INDEX (-1) 213 #define FP_ARG_COUNT_INDEX (-2) 214 #define FP_OLD_FP_INDEX (-3) 215 #define FP_RET_PC_INDEX (-4) 216 #define FP_TO_ARGS_DIST (4) /* should be 0 - (above index) */ 217 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex)) 218 #define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_CACHE_INDEX)) 219 #define FP_GET_ARG_COUNT(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_COUNT_INDEX).val.n) 220 #define FP_GET_OLD_FP(xFrameP) ((FP_GET_ITEM(xFrameP, FP_OLD_FP_INDEX)).val.dataval) 221 #define FP_GET_RET_PC(xFrameP) ((FP_GET_ITEM(xFrameP, FP_RET_PC_INDEX)).val.inst) 222 #define FP_ARG_START_INDEX(xFrameP) (-(FP_GET_ARG_COUNT(xFrameP) + FP_TO_ARGS_DIST)) 223 #define FP_GET_ARG_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN + FP_ARG_START_INDEX(xFrameP))) 224 #define FP_GET_SYM_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN)) 225 #define FP_GET_SYM_VAL(xFrameP,xSym) (FP_GET_SYM_N(xFrameP, xSym->value.val.n)) 226 227 /* 228 ** Initialize macro language global variables. Must be called before 229 ** any macros are even parsed, because the parser uses action routine 230 ** symbols to comprehend hyphenated names. 231 */ 232 void InitMacroGlobals(void) 233 { 234 XtActionsRec *actions; 235 int i, nActions; 236 static char argName[3] = "$x"; 237 static DataValue dv = {NO_TAG, {0}}; 238 239 /* Add action routines from NEdit menus and text widget */ 240 actions = GetMenuActions(&nActions); 241 for (i=0; i<nActions; i++) { 242 dv.val.xtproc = actions[i].proc; 243 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv); 244 } 245 actions = TextGetActions(&nActions); 246 for (i=0; i<nActions; i++) { 247 dv.val.xtproc = actions[i].proc; 248 InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv); 249 } 250 251 /* Add subroutine argument symbols ($1, $2, ..., $9) */ 252 for (i=0; i<9; i++) { 253 argName[1] = '1' + i; 254 dv.val.n = i; 255 InstallSymbol(argName, ARG_SYM, dv); 256 } 257 258 /* Add special symbol $n_args */ 259 dv.val.n = N_ARGS_ARG_SYM; 260 InstallSymbol("$n_args", ARG_SYM, dv); 261 } 262 263 /* 264 ** To build a program for the interpreter, call BeginCreatingProgram, to 265 ** begin accumulating the program, followed by calls to AddOp, AddSym, 266 ** and InstallSymbol to add symbols and operations. When the new program 267 ** is finished, collect the results with FinishCreatingProgram. This returns 268 ** a self contained program that can be run with ExecuteMacro. 269 */ 270 271 /* 272 ** Start collecting instructions for a program. Clears the program 273 ** and the symbol table. 274 */ 275 void BeginCreatingProgram(void) 276 { 277 LocalSymList = NULL; 278 ProgP = Prog; 279 LoopStackPtr = LoopStack; 280 } 281 282 /* 283 ** Finish up the program under construction, and return it (code and 284 ** symbol table) as a package that ExecuteMacro can execute. This 285 ** program must be freed with FreeProgram. 286 */ 287 Program *FinishCreatingProgram(void) 288 { 289 Program *newProg; 290 int progLen, fpOffset = 0; 291 Symbol *s; 292 293 newProg = (Program *)NEditMalloc(sizeof(Program)); 294 progLen = ((char *)ProgP) - ((char *)Prog); 295 newProg->code = (Inst *)NEditMalloc(progLen); 296 memcpy(newProg->code, Prog, progLen); 297 newProg->localSymList = LocalSymList; 298 LocalSymList = NULL; 299 300 /* Local variables' values are stored on the stack. Here we assign 301 frame pointer offsets to them. */ 302 for (s = newProg->localSymList; s != NULL; s = s->next) 303 s->value.val.n = fpOffset++; 304 305 DISASM(newProg->code, ProgP - Prog); 306 307 return newProg; 308 } 309 310 void FreeProgram(Program *prog) 311 { 312 freeSymbolTable(prog->localSymList); 313 NEditFree(prog->code); 314 NEditFree(prog); 315 } 316 317 /* 318 ** Add an operator (instruction) to the end of the current program 319 */ 320 int AddOp(int op, char **msg) 321 { 322 if (ProgP >= &Prog[PROGRAM_SIZE]) { 323 *msg = "macro too large"; 324 return 0; 325 } 326 ProgP->func = OpFns[op]; 327 ProgP++; 328 return 1; 329 } 330 331 /* 332 ** Add a symbol operand to the current program 333 */ 334 int AddSym(Symbol *sym, char **msg) 335 { 336 if (ProgP >= &Prog[PROGRAM_SIZE]) { 337 *msg = "macro too large"; 338 return 0; 339 } 340 ProgP->sym = sym; 341 ProgP++; 342 return 1; 343 } 344 345 /* 346 ** Add an immediate value operand to the current program 347 */ 348 int AddImmediate(int value, char **msg) 349 { 350 if (ProgP >= &Prog[PROGRAM_SIZE]) { 351 *msg = "macro too large"; 352 return 0; 353 } 354 ProgP->value = value; 355 ProgP++; 356 return 1; 357 } 358 359 /* 360 ** Add a branch offset operand to the current program 361 */ 362 int AddBranchOffset(Inst *to, char **msg) 363 { 364 if (ProgP >= &Prog[PROGRAM_SIZE]) { 365 *msg = "macro too large"; 366 return 0; 367 } 368 /* Should be ptrdiff_t for branch offsets */ 369 ProgP->value = to - ProgP; 370 ProgP++; 371 372 return 1; 373 } 374 375 /* 376 ** Return the address at which the next instruction will be stored 377 */ 378 Inst *GetPC(void) 379 { 380 return ProgP; 381 } 382 383 /* 384 ** Swap the positions of two contiguous blocks of code. The first block 385 ** running between locations start and boundary, and the second between 386 ** boundary and end. 387 */ 388 void SwapCode(Inst *start, Inst *boundary, Inst *end) 389 { 390 #define reverseCode(L, H) \ 391 do { register Inst t, *l = L, *h = H - 1; \ 392 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0) 393 /* double-reverse method: reverse elements of both parts then whole lot */ 394 /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */ 395 reverseCode(start, boundary); /* 1 */ 396 reverseCode(boundary, end); /* 2 */ 397 reverseCode(start, end); /* 3 */ 398 } 399 400 /* 401 ** Maintain a stack to save addresses of branch operations for break and 402 ** continue statements, so they can be filled in once the information 403 ** on where to branch is known. 404 ** 405 ** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or 406 ** AddContinueAddr to register the address at which to store the branch 407 ** address for a break or continue statement, and FillLoopAddrs to fill 408 ** in all the addresses and return to the level of the enclosing loop. 409 */ 410 void StartLoopAddrList(void) 411 { 412 addLoopAddr(NULL); 413 } 414 415 int AddBreakAddr(Inst *addr) 416 { 417 if (LoopStackPtr == LoopStack) return 1; 418 addLoopAddr(addr); 419 addr->value = NEEDS_BREAK; 420 return 0; 421 } 422 423 int AddContinueAddr(Inst *addr) 424 { 425 if (LoopStackPtr == LoopStack) return 1; 426 addLoopAddr(addr); 427 addr->value = NEEDS_CONTINUE; 428 return 0; 429 } 430 431 static void addLoopAddr(Inst *addr) 432 { 433 if (LoopStackPtr > &LoopStack[LOOP_STACK_SIZE-1]) { 434 fprintf(stderr, "NEdit: loop stack overflow in macro parser"); 435 return; 436 } 437 *LoopStackPtr++ = addr; 438 } 439 440 void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr) 441 { 442 while (True) { 443 LoopStackPtr--; 444 if (LoopStackPtr < LoopStack) { 445 fprintf(stderr, "NEdit: internal error (lsu) in macro parser\n"); 446 return; 447 } 448 if (*LoopStackPtr == NULL) 449 break; 450 if ((*LoopStackPtr)->value == NEEDS_BREAK) 451 (*LoopStackPtr)->value = breakAddr - *LoopStackPtr; 452 else if ((*LoopStackPtr)->value == NEEDS_CONTINUE) 453 (*LoopStackPtr)->value = continueAddr - *LoopStackPtr; 454 else 455 fprintf(stderr, "NEdit: internal error (uat) in macro parser\n"); 456 } 457 } 458 459 /* 460 ** Execute a compiled macro, "prog", using the arguments in the array 461 ** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR. 462 ** if MACRO_DONE is returned, the macro completed, and the returned value 463 ** (if any) can be read from "result". If MACRO_PREEMPT is returned, the 464 ** macro exceeded its alotted time-slice and scheduled... 465 */ 466 int ExecuteMacro(WindowInfo *window, Program *prog, int nArgs, DataValue *args, 467 DataValue *result, RestartData **continuation, char **msg) 468 { 469 RestartData *context; 470 static DataValue noValue = {NO_TAG, {0}}; 471 Symbol *s; 472 int i; 473 474 /* Create an execution context (a stack, a stack pointer, a frame pointer, 475 and a program counter) which will retain the program state across 476 preemption and resumption of execution */ 477 context = (RestartData *)NEditMalloc(sizeof(RestartData)); 478 context->stack = (DataValue *)NEditMalloc(sizeof(DataValue) * STACK_SIZE); 479 *continuation = context; 480 context->stackP = context->stack; 481 context->pc = prog->code; 482 context->runWindow = window; 483 context->focusWindow = window; 484 485 /* Push arguments and call information onto the stack */ 486 for (i=0; i<nArgs; i++) 487 *(context->stackP++) = args[i]; 488 489 context->stackP->val.subr = NULL; /* return PC */ 490 context->stackP->tag = NO_TAG; 491 context->stackP++; 492 493 *(context->stackP++) = noValue; /* old FrameP */ 494 495 context->stackP->tag = NO_TAG; /* nArgs */ 496 context->stackP->val.n = nArgs; 497 context->stackP++; 498 499 *(context->stackP++) = noValue; /* cached arg array */ 500 501 context->frameP = context->stackP; 502 503 /* Initialize and make room on the stack for local variables */ 504 for (s = prog->localSymList; s != NULL; s = s->next) { 505 FP_GET_SYM_VAL(context->frameP, s) = noValue; 506 context->stackP++; 507 } 508 509 /* Begin execution, return on error or preemption */ 510 return ContinueMacro(context, result, msg); 511 } 512 513 /* 514 ** Continue the execution of a suspended macro whose state is described in 515 ** "continuation" 516 */ 517 int ContinueMacro(RestartData *continuation, DataValue *result, char **msg) 518 { 519 register int status, instCount = 0; 520 register Inst *inst; 521 RestartData oldContext; 522 523 /* To allow macros to be invoked arbitrarily (such as those automatically 524 triggered within smart-indent) within executing macros, this call is 525 reentrant. */ 526 saveContext(&oldContext); 527 528 /* 529 ** Execution Loop: Call the succesive routine addresses in the program 530 ** until one returns something other than STAT_OK, then take action 531 */ 532 restoreContext(continuation); 533 ErrMsg = NULL; 534 for (;;) { 535 536 /* Execute an instruction */ 537 inst = PC++; 538 status = (inst->func)(); 539 540 /* If error return was not STAT_OK, return to caller */ 541 if (status != STAT_OK) { 542 if (status == STAT_PREEMPT) { 543 saveContext(continuation); 544 restoreContext(&oldContext); 545 return MACRO_PREEMPT; 546 } else if (status == STAT_ERROR) { 547 *msg = ErrMsg; 548 FreeRestartData(continuation); 549 restoreContext(&oldContext); 550 return MACRO_ERROR; 551 } else if (status == STAT_DONE) { 552 *msg = ""; 553 *result = *--StackP; 554 FreeRestartData(continuation); 555 restoreContext(&oldContext); 556 return MACRO_DONE; 557 } 558 } 559 560 /* Count instructions executed. If the instruction limit is hit, 561 preempt, store re-start information in continuation and give 562 X, other macros, and other shell scripts a chance to execute */ 563 instCount++; 564 if (instCount >= INSTRUCTION_LIMIT) { 565 saveContext(continuation); 566 restoreContext(&oldContext); 567 return MACRO_TIME_LIMIT; 568 } 569 } 570 } 571 572 /* 573 ** If a macro is already executing, and requests that another macro be run, 574 ** this can be called instead of ExecuteMacro to run it in the same context 575 ** as if it were a subroutine. This saves the caller from maintaining 576 ** separate contexts, and serializes processing of the two macros without 577 ** additional work. 578 */ 579 void RunMacroAsSubrCall(Program *prog) 580 { 581 Symbol *s; 582 static DataValue noValue = {NO_TAG, {0}}; 583 584 /* See subroutine "callSubroutine" for a description of the stack frame 585 for a subroutine call */ 586 StackP->tag = NO_TAG; 587 StackP->val.inst = PC; /* return PC */ 588 StackP++; 589 590 StackP->tag = NO_TAG; 591 StackP->val.dataval = FrameP; /* old FrameP */ 592 StackP++; 593 594 StackP->tag = NO_TAG; /* nArgs */ 595 StackP->val.n = 0; 596 StackP++; 597 598 *(StackP++) = noValue; /* cached arg array */ 599 600 FrameP = StackP; 601 PC = prog->code; 602 for (s = prog->localSymList; s != NULL; s = s->next) { 603 FP_GET_SYM_VAL(FrameP, s) = noValue; 604 StackP++; 605 } 606 } 607 608 void FreeRestartData(RestartData *context) 609 { 610 NEditFree(context->stack); 611 NEditFree(context); 612 } 613 614 /* 615 ** Cause a macro in progress to be preempted (called by commands which take 616 ** a long time, or want to return to the event loop. Call ResumeMacroExecution 617 ** to resume. 618 */ 619 void PreemptMacro(void) 620 { 621 PreemptRequest = True; 622 } 623 624 /* 625 ** Reset the return value for a subroutine which caused preemption (this is 626 ** how to return a value from a routine which preempts instead of returning 627 ** a value directly). 628 */ 629 void ModifyReturnedValue(RestartData *context, DataValue dv) 630 { 631 if ((context->pc-1)->func == fetchRetVal) 632 *(context->stackP-1) = dv; 633 } 634 635 /* 636 ** Called within a routine invoked from a macro, returns the window in 637 ** which the macro is executing (where the banner is, not where it is focused) 638 */ 639 WindowInfo *MacroRunWindow(void) 640 { 641 return InitiatingWindow; 642 } 643 644 /* 645 ** Called within a routine invoked from a macro, returns the window to which 646 ** the currently executing macro is focused (the window which macro commands 647 ** modify, not the window from which the macro is being run) 648 */ 649 WindowInfo *MacroFocusWindow(void) 650 { 651 return FocusWindow; 652 } 653 654 /* 655 ** Set the window to which macro subroutines and actions which operate on an 656 ** implied window are directed. 657 */ 658 void SetMacroFocusWindow(WindowInfo *window) 659 { 660 FocusWindow = window; 661 } 662 663 /* 664 ** install an array iteration symbol 665 ** it is tagged as an integer but holds an array node pointer 666 */ 667 #define ARRAY_ITER_SYM_PREFIX "aryiter " 668 Symbol *InstallIteratorSymbol(void) 669 { 670 char symbolName[sizeof(ARRAY_ITER_SYM_PREFIX) + TYPE_INT_STR_SIZE(int)]; 671 DataValue value; 672 static int interatorNameIndex = 0; 673 674 sprintf(symbolName, ARRAY_ITER_SYM_PREFIX "#%d", interatorNameIndex); 675 ++interatorNameIndex; 676 value.tag = INT_TAG; 677 value.val.arrayPtr = NULL; 678 return(InstallSymbol(symbolName, LOCAL_SYM, value)); 679 } 680 681 /* 682 ** Lookup a constant string by its value. This allows reuse of string 683 ** constants and fixing a leak in the interpreter. 684 */ 685 Symbol *LookupStringConstSymbol(const char *value) 686 { 687 Symbol *s; 688 689 for (s = GlobalSymList; s != NULL; s = s->next) { 690 if (s->type == CONST_SYM && 691 s->value.tag == STRING_TAG && 692 !strcmp(s->value.val.str.rep, value)) { 693 return(s); 694 } 695 } 696 return(NULL); 697 } 698 699 /* 700 ** install string str in the global symbol table with a string name 701 */ 702 Symbol *InstallStringConstSymbol(const char *str) 703 { 704 static int stringConstIndex = 0; 705 char stringName[35]; 706 DataValue value; 707 Symbol *sym = LookupStringConstSymbol(str); 708 if (sym) { 709 return sym; 710 } 711 712 sprintf(stringName, "string #%d", stringConstIndex++); 713 value.tag = STRING_TAG; 714 AllocNStringCpy(&value.val.str, str); 715 return(InstallSymbol(stringName, CONST_SYM, value)); 716 } 717 718 /* 719 ** find a symbol in the symbol table 720 */ 721 Symbol *LookupSymbol(const char *name) 722 { 723 Symbol *s; 724 725 for (s = LocalSymList; s != NULL; s = s->next) 726 if (strcmp(s->name, name) == 0) 727 return s; 728 for (s = GlobalSymList; s != NULL; s = s->next) 729 if (strcmp(s->name, name) == 0) 730 return s; 731 return NULL; 732 } 733 734 /* 735 ** install symbol name in symbol table 736 */ 737 Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value) 738 { 739 Symbol *s; 740 741 s = (Symbol *)NEditMalloc(sizeof(Symbol)); 742 s->name = NEditStrdup(name); 743 s->type = type; 744 s->value = value; 745 if (type == LOCAL_SYM) { 746 s->next = LocalSymList; 747 LocalSymList = s; 748 } else { 749 s->next = GlobalSymList; 750 GlobalSymList = s; 751 } 752 return s; 753 } 754 755 /* 756 ** Promote a symbol from local to global, removing it from the local symbol 757 ** list. 758 ** 759 ** This is used as a forward declaration feature for macro functions. 760 ** If a function is called (ie while parsing the macro) where the 761 ** function isn't defined yet, the symbol is put into the GlobalSymList 762 ** so that the function definition uses the same symbol. 763 ** 764 */ 765 Symbol *PromoteToGlobal(Symbol *sym) 766 { 767 Symbol *s; 768 769 if (sym->type != LOCAL_SYM) 770 return sym; 771 772 /* Remove sym from the local symbol list */ 773 if (sym == LocalSymList) 774 LocalSymList = sym->next; 775 else { 776 for (s = LocalSymList; s != NULL; s = s->next) { 777 if (s->next == sym) { 778 s->next = sym->next; 779 break; 780 } 781 } 782 } 783 784 /* There are two scenarios which could make this check succeed: 785 a) this sym is in the GlobalSymList as a LOCAL_SYM symbol 786 b) there is another symbol as a non-LOCAL_SYM in the GlobalSymList 787 Both are errors, without question. 788 We currently just print this warning, but we should error out the 789 parsing process. */ 790 s = LookupSymbol(sym->name); 791 if (sym == s) { 792 /* case a) 793 just make this symbol a GLOBAL_SYM symbol and return */ 794 fprintf(stderr, 795 "xnedit: To boldly go where no local sym has gone before: %s\n", 796 sym->name); 797 sym->type = GLOBAL_SYM; 798 return sym; 799 } else if (NULL != s) { 800 /* case b) 801 sym will shadow the old symbol from the GlobalSymList */ 802 fprintf(stderr, 803 "xnedit: duplicate symbol in LocalSymList and GlobalSymList: %s\n", 804 sym->name); 805 } 806 807 /* Add the symbol directly to the GlobalSymList, because InstallSymbol() 808 will allocate a new Symbol, which results in a memory leak of sym. 809 Don't use MACRO_FUNCTION_SYM as type, because in 810 macro.c:readCheckMacroString() we use ProgramFree() for the .val.prog, 811 but this symbol has no program attached and ProgramFree() is not NULL 812 pointer safe */ 813 sym->type = GLOBAL_SYM; 814 sym->next = GlobalSymList; 815 GlobalSymList = sym; 816 817 return sym; 818 } 819 820 /* 821 ** Allocate memory for a string, and keep track of it, such that it 822 ** can be recovered later using GarbageCollectStrings. (A linked list 823 ** of pointers is maintained by threading through the memory behind 824 ** the returned pointers). Length does not include the terminating null 825 ** character, so to allocate space for a string of strlen == n, you must 826 ** use AllocString(n+1). 827 */ 828 829 /*#define TRACK_GARBAGE_LEAKS*/ 830 #ifdef TRACK_GARBAGE_LEAKS 831 static int numAllocatedStrings = 0; 832 static int numAllocatedSparseArrayElements = 0; 833 #endif 834 835 /* Allocate a new string buffer of length chars */ 836 char *AllocString(int length) 837 { 838 char *mem; 839 840 mem = (char*)NEditMalloc(length + sizeof(char *) + 1); 841 *((char **)mem) = AllocatedStrings; 842 AllocatedStrings = mem; 843 #ifdef TRACK_GARBAGE_LEAKS 844 ++numAllocatedStrings; 845 #endif 846 return mem + sizeof(char *) + 1; 847 } 848 849 /* 850 * Allocate a new NString buffer of length chars (terminating \0 included), 851 * The buffer length is initialized to length-1 and the terminating \0 is 852 * filled in. 853 */ 854 int AllocNString(NString *string, int length) 855 { 856 char *mem; 857 858 mem = (char*)NEditMalloc(length + sizeof(char *) + 1); 859 if (!mem) { 860 string->rep = 0; 861 string->len = 0; 862 return False; 863 } 864 865 *((char **)mem) = AllocatedStrings; 866 AllocatedStrings = mem; 867 #ifdef TRACK_GARBAGE_LEAKS 868 ++numAllocatedStrings; 869 #endif 870 string->rep = mem + sizeof(char *) + 1; 871 string->rep[length-1] = '\0'; /* forced \0 */ 872 string->len = length-1; 873 return True; 874 } 875 876 /* Allocate a new string buffer of length chars, and copy in the string s */ 877 char *AllocStringNCpy(const char *s, int length) 878 { 879 char *p = AllocString(length + 1); /* add extra char for forced \0 */ 880 if (!p) 881 return p; 882 if (!s) 883 s = ""; 884 p[length] = '\0'; /* forced \0 */ 885 return strncpy(p, s, length); 886 } 887 888 /* 889 * Allocate a new NString buffer of length chars (terminating \0 NOT included), 890 * and copy at most length characters of the given string. 891 * The buffer length is properly set and the buffer is guaranteed to be 892 * \0-terminated. 893 */ 894 int AllocNStringNCpy(NString *string, const char *s, int length) 895 { 896 if (!AllocNString(string, length + 1)) /* add extra char for forced \0 */ 897 return False; 898 if (!s) 899 s = ""; 900 strncpy(string->rep, s, length); 901 string->len = strlen(string->rep); /* re-calculate! */ 902 return True; 903 } 904 905 /* Allocate a new copy of string s */ 906 char *AllocStringCpy(const char *s) 907 { 908 return AllocStringNCpy(s, s ? strlen(s) : 0); 909 } 910 911 /* 912 * Allocate a new NString buffer, containing a copy of the given string. 913 * The length is set to the length of the string and resulting string is 914 * guaranteed to be \0-terminated. 915 */ 916 int AllocNStringCpy(NString *string, const char *s) 917 { 918 size_t length = s ? strlen(s) : 0; 919 if (!AllocNString(string, length + 1)) 920 return False; 921 if (s) 922 strncpy(string->rep, s, length); 923 return True; 924 } 925 926 static SparseArrayEntry *allocateSparseArrayEntry(void) 927 { 928 SparseArrayEntryWrapper *mem; 929 930 mem = (SparseArrayEntryWrapper *)NEditMalloc(sizeof(SparseArrayEntryWrapper)); 931 mem->next = AllocatedSparseArrayEntries; 932 AllocatedSparseArrayEntries = mem; 933 #ifdef TRACK_GARBAGE_LEAKS 934 ++numAllocatedSparseArrayElements; 935 #endif 936 return(&(mem->data)); 937 } 938 939 static void MarkArrayContentsAsUsed(SparseArrayEntry *arrayPtr) 940 { 941 SparseArrayEntry *globalSEUse; 942 943 if (arrayPtr) { 944 ((SparseArrayEntryWrapper *)arrayPtr)->inUse = 1; 945 for (globalSEUse = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)arrayPtr); 946 globalSEUse != NULL; 947 globalSEUse = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)globalSEUse)) { 948 949 ((SparseArrayEntryWrapper *)globalSEUse)->inUse = 1; 950 /* test first because it may be read-only static string */ 951 if (!(*(globalSEUse->key - 1))) { 952 *(globalSEUse->key - 1) = 1; 953 } 954 if (globalSEUse->value.tag == STRING_TAG) { 955 /* test first because it may be read-only static string */ 956 if (!(*(globalSEUse->value.val.str.rep - 1))) { 957 *(globalSEUse->value.val.str.rep - 1) = 1; 958 } 959 } 960 else if (globalSEUse->value.tag == ARRAY_TAG) { 961 MarkArrayContentsAsUsed(globalSEUse->value.val.arrayPtr); 962 } 963 } 964 } 965 } 966 967 /* 968 ** Collect strings that are no longer referenced from the global symbol 969 ** list. THIS CAN NOT BE RUN WHILE ANY MACROS ARE EXECUTING. It must 970 ** only be run after all macro activity has ceased. 971 */ 972 973 void GarbageCollectStrings(void) 974 { 975 SparseArrayEntryWrapper *nextAP, *thisAP; 976 char *p, *next; 977 Symbol *s; 978 979 /* mark all strings as unreferenced */ 980 for (p = AllocatedStrings; p != NULL; p = *((char **)p)) { 981 *(p + sizeof(char *)) = 0; 982 } 983 984 for (thisAP = AllocatedSparseArrayEntries; 985 thisAP != NULL; thisAP = thisAP->next) { 986 thisAP->inUse = 0; 987 } 988 989 /* Sweep the global symbol list, marking which strings are still 990 referenced */ 991 for (s = GlobalSymList; s != NULL; s = s->next) { 992 if (s->value.tag == STRING_TAG) { 993 /* test first because it may be read-only static string */ 994 if (!(*(s->value.val.str.rep - 1))) { 995 *(s->value.val.str.rep - 1) = 1; 996 } 997 } 998 else if (s->value.tag == ARRAY_TAG) { 999 MarkArrayContentsAsUsed(s->value.val.arrayPtr); 1000 } 1001 } 1002 1003 /* Collect all of the strings which remain unreferenced */ 1004 next = AllocatedStrings; 1005 AllocatedStrings = NULL; 1006 while (next != NULL) { 1007 p = next; 1008 next = *((char **)p); 1009 if (*(p + sizeof(char *)) != 0) { 1010 *((char **)p) = AllocatedStrings; 1011 AllocatedStrings = p; 1012 } 1013 else { 1014 #ifdef TRACK_GARBAGE_LEAKS 1015 --numAllocatedStrings; 1016 #endif 1017 NEditFree(p); 1018 } 1019 } 1020 1021 nextAP = AllocatedSparseArrayEntries; 1022 AllocatedSparseArrayEntries = NULL; 1023 while (nextAP != NULL) { 1024 thisAP = nextAP; 1025 nextAP = nextAP->next; 1026 if (thisAP->inUse != 0) { 1027 thisAP->next = AllocatedSparseArrayEntries; 1028 AllocatedSparseArrayEntries = thisAP; 1029 } 1030 else { 1031 #ifdef TRACK_GARBAGE_LEAKS 1032 --numAllocatedSparseArrayElements; 1033 #endif 1034 NEditFree(thisAP); 1035 } 1036 } 1037 1038 #ifdef TRACK_GARBAGE_LEAKS 1039 printf("str count = %d\nary count = %d\n", numAllocatedStrings, numAllocatedSparseArrayElements); 1040 #endif 1041 } 1042 1043 /* 1044 ** Save and restore execution context to data structure "context" 1045 */ 1046 static void saveContext(RestartData *context) 1047 { 1048 context->stack = TheStack; 1049 context->stackP = StackP; 1050 context->frameP = FrameP; 1051 context->pc = PC; 1052 context->runWindow = InitiatingWindow; 1053 context->focusWindow = FocusWindow; 1054 } 1055 1056 static void restoreContext(RestartData *context) 1057 { 1058 TheStack = context->stack; 1059 StackP = context->stackP; 1060 FrameP = context->frameP; 1061 PC = context->pc; 1062 InitiatingWindow = context->runWindow; 1063 FocusWindow = context->focusWindow; 1064 } 1065 1066 static void freeSymbolTable(Symbol *symTab) 1067 { 1068 Symbol *s; 1069 1070 while(symTab != NULL) { 1071 s = symTab; 1072 free(s->name); 1073 symTab = s->next; 1074 NEditFree(s); 1075 } 1076 } 1077 1078 #define POP(dataVal) \ 1079 if (StackP == TheStack) \ 1080 return execError(StackUnderflowMsg, ""); \ 1081 dataVal = *--StackP; 1082 1083 #define PUSH(dataVal) \ 1084 if (StackP >= &TheStack[STACK_SIZE]) \ 1085 return execError(StackOverflowMsg, ""); \ 1086 *StackP++ = dataVal; 1087 1088 #define PEEK(dataVal, peekIndex) \ 1089 dataVal = *(StackP - peekIndex - 1); 1090 1091 #define POP_INT(number) \ 1092 if (StackP == TheStack) \ 1093 return execError(StackUnderflowMsg, ""); \ 1094 --StackP; \ 1095 if (StackP->tag == STRING_TAG) { \ 1096 if (!StringToNum(StackP->val.str.rep, &number)) \ 1097 return execError(StringToNumberMsg, ""); \ 1098 } else if (StackP->tag == INT_TAG) \ 1099 number = StackP->val.n; \ 1100 else \ 1101 return(execError("can''t convert array to integer", NULL)); 1102 1103 #define POP_STRING(string) \ 1104 if (StackP == TheStack) \ 1105 return execError(StackUnderflowMsg, ""); \ 1106 --StackP; \ 1107 if (StackP->tag == INT_TAG) { \ 1108 string = AllocString(TYPE_INT_STR_SIZE(int)); \ 1109 sprintf(string, "%d", StackP->val.n); \ 1110 } else if (StackP->tag == STRING_TAG) \ 1111 string = StackP->val.str.rep; \ 1112 else \ 1113 return(execError("can''t convert array to string", NULL)); 1114 1115 #define PEEK_STRING(string, peekIndex) \ 1116 if ((StackP - peekIndex - 1)->tag == INT_TAG) { \ 1117 string = AllocString(TYPE_INT_STR_SIZE(int)); \ 1118 sprintf(string, "%d", (StackP - peekIndex - 1)->val.n); \ 1119 } \ 1120 else if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \ 1121 string = (StackP - peekIndex - 1)->val.str.rep; \ 1122 } \ 1123 else { \ 1124 return(execError("can''t convert array to string", NULL)); \ 1125 } 1126 1127 #define PEEK_INT(number, peekIndex) \ 1128 if ((StackP - peekIndex - 1)->tag == STRING_TAG) { \ 1129 if (!StringToNum((StackP - peekIndex - 1)->val.str.rep, &number)) { \ 1130 return execError(StringToNumberMsg, ""); \ 1131 } \ 1132 } else if ((StackP - peekIndex - 1)->tag == INT_TAG) { \ 1133 number = (StackP - peekIndex - 1)->val.n; \ 1134 } \ 1135 else { \ 1136 return(execError("can''t convert array to string", NULL)); \ 1137 } 1138 1139 #define PUSH_INT(number) \ 1140 if (StackP >= &TheStack[STACK_SIZE]) \ 1141 return execError(StackOverflowMsg, ""); \ 1142 StackP->tag = INT_TAG; \ 1143 StackP->val.n = number; \ 1144 StackP++; 1145 1146 #define PUSH_STRING(string, length) \ 1147 if (StackP >= &TheStack[STACK_SIZE]) \ 1148 return execError(StackOverflowMsg, ""); \ 1149 StackP->tag = STRING_TAG; \ 1150 StackP->val.str.rep = string; \ 1151 StackP->val.str.len = length; \ 1152 StackP++; 1153 1154 #define BINARY_NUMERIC_OPERATION(operator) \ 1155 int n1, n2; \ 1156 DISASM_RT(PC-1, 1); \ 1157 STACKDUMP(2, 3); \ 1158 POP_INT(n2) \ 1159 POP_INT(n1) \ 1160 PUSH_INT(n1 operator n2) \ 1161 return STAT_OK; 1162 1163 #define UNARY_NUMERIC_OPERATION(operator) \ 1164 int n; \ 1165 DISASM_RT(PC-1, 1); \ 1166 STACKDUMP(1, 3); \ 1167 POP_INT(n) \ 1168 PUSH_INT(operator n) \ 1169 return STAT_OK; 1170 1171 /* 1172 ** copy a symbol's value onto the stack 1173 ** Before: Prog-> [Sym], next, ... 1174 ** TheStack-> next, ... 1175 ** After: Prog-> Sym, [next], ... 1176 ** TheStack-> [symVal], next, ... 1177 */ 1178 static int pushSymVal(void) 1179 { 1180 Symbol *s; 1181 int nArgs, argNum; 1182 DataValue symVal; 1183 1184 DISASM_RT(PC-1, 2); 1185 STACKDUMP(0, 3); 1186 1187 s = PC->sym; 1188 PC++; 1189 1190 if (s->type == LOCAL_SYM) { 1191 symVal = FP_GET_SYM_VAL(FrameP, s); 1192 } else if (s->type == GLOBAL_SYM || s->type == CONST_SYM) { 1193 symVal = s->value; 1194 } else if (s->type == ARG_SYM) { 1195 nArgs = FP_GET_ARG_COUNT(FrameP); 1196 argNum = s->value.val.n; 1197 if (argNum >= nArgs) { 1198 return execError("referenced undefined argument: %s", s->name); 1199 } 1200 if (argNum == N_ARGS_ARG_SYM) { 1201 symVal.tag = INT_TAG; 1202 symVal.val.n = nArgs; 1203 } 1204 else { 1205 symVal = FP_GET_ARG_N(FrameP, argNum); 1206 } 1207 } else if (s->type == PROC_VALUE_SYM) { 1208 char *errMsg; 1209 if (!(s->value.val.subr)(FocusWindow, NULL, 0, 1210 &symVal, &errMsg)) { 1211 return execError(errMsg, s->name); 1212 } 1213 } else 1214 return execError("reading non-variable: %s", s->name); 1215 if (symVal.tag == NO_TAG) { 1216 return execError("variable not set: %s", s->name); 1217 } 1218 1219 PUSH(symVal) 1220 1221 return STAT_OK; 1222 } 1223 1224 static int pushArgVal(void) 1225 { 1226 int nArgs, argNum; 1227 1228 DISASM_RT(PC-1, 1); 1229 STACKDUMP(1, 3); 1230 1231 POP_INT(argNum) 1232 --argNum; 1233 nArgs = FP_GET_ARG_COUNT(FrameP); 1234 if (argNum >= nArgs || argNum < 0) { 1235 char argStr[TYPE_INT_STR_SIZE(argNum)]; 1236 sprintf(argStr, "%d", argNum + 1); 1237 return execError("referenced undefined argument: $args[%s]", argStr); 1238 } 1239 PUSH(FP_GET_ARG_N(FrameP, argNum)); 1240 return STAT_OK; 1241 } 1242 1243 static int pushArgCount(void) 1244 { 1245 DISASM_RT(PC-1, 1); 1246 STACKDUMP(0, 3); 1247 1248 PUSH_INT(FP_GET_ARG_COUNT(FrameP)); 1249 return STAT_OK; 1250 } 1251 1252 static int pushArgArray(void) 1253 { 1254 int nArgs, argNum; 1255 DataValue argVal, *resultArray; 1256 1257 DISASM_RT(PC-1, 1); 1258 STACKDUMP(0, 3); 1259 1260 nArgs = FP_GET_ARG_COUNT(FrameP); 1261 resultArray = &FP_GET_ARG_ARRAY_CACHE(FrameP); 1262 if (resultArray->tag != ARRAY_TAG) { 1263 resultArray->tag = ARRAY_TAG; 1264 resultArray->val.arrayPtr = ArrayNew(); 1265 1266 for (argNum = 0; argNum < nArgs; ++argNum) { 1267 char intStr[TYPE_INT_STR_SIZE(argNum)]; 1268 1269 sprintf(intStr, "%d", argNum + 1); 1270 argVal = FP_GET_ARG_N(FrameP, argNum); 1271 if (!ArrayInsert(resultArray, AllocStringCpy(intStr), &argVal)) { 1272 return(execError("array insertion failure", NULL)); 1273 } 1274 } 1275 } 1276 PUSH(*resultArray); 1277 return STAT_OK; 1278 } 1279 1280 /* 1281 ** Push an array (by reference) onto the stack 1282 ** Before: Prog-> [ArraySym], makeEmpty, next, ... 1283 ** TheStack-> next, ... 1284 ** After: Prog-> ArraySym, makeEmpty, [next], ... 1285 ** TheStack-> [elemValue], next, ... 1286 ** makeEmpty is either true (1) or false (0): if true, and the element is not 1287 ** present in the array, create it. 1288 */ 1289 static int pushArraySymVal(void) 1290 { 1291 Symbol *sym; 1292 DataValue *dataPtr; 1293 int initEmpty; 1294 1295 DISASM_RT(PC-1, 3); 1296 STACKDUMP(0, 3); 1297 1298 sym = PC->sym; 1299 PC++; 1300 initEmpty = PC->value; 1301 PC++; 1302 1303 if (sym->type == LOCAL_SYM) { 1304 dataPtr = &FP_GET_SYM_VAL(FrameP, sym); 1305 } 1306 else if (sym->type == GLOBAL_SYM) { 1307 dataPtr = &sym->value; 1308 } 1309 else { 1310 return execError("assigning to non-lvalue array or non-array: %s", sym->name); 1311 } 1312 1313 if (initEmpty && dataPtr->tag == NO_TAG) { 1314 dataPtr->tag = ARRAY_TAG; 1315 dataPtr->val.arrayPtr = ArrayNew(); 1316 } 1317 1318 if (dataPtr->tag == NO_TAG) { 1319 return execError("variable not set: %s", sym->name); 1320 } 1321 1322 PUSH(*dataPtr) 1323 1324 return STAT_OK; 1325 } 1326 1327 /* 1328 ** assign top value to next symbol 1329 ** 1330 ** Before: Prog-> [symbol], next, ... 1331 ** TheStack-> [value], next, ... 1332 ** After: Prog-> symbol, [next], ... 1333 ** TheStack-> next, ... 1334 */ 1335 static int assign(void) 1336 { 1337 Symbol *sym; 1338 DataValue *dataPtr; 1339 DataValue value; 1340 1341 DISASM_RT(PC-1, 2); 1342 STACKDUMP(1, 3); 1343 1344 sym = PC->sym; 1345 PC++; 1346 1347 if (sym->type != GLOBAL_SYM && sym->type != LOCAL_SYM) { 1348 if (sym->type == ARG_SYM) { 1349 return execError("assignment to function argument: %s", sym->name); 1350 } 1351 else if (sym->type == PROC_VALUE_SYM) { 1352 return execError("assignment to read-only variable: %s", sym->name); 1353 } 1354 else { 1355 return execError("assignment to non-variable: %s", sym->name); 1356 } 1357 } 1358 1359 if (sym->type == LOCAL_SYM) { 1360 dataPtr = &FP_GET_SYM_VAL(FrameP, sym); 1361 } 1362 else { 1363 dataPtr = &sym->value; 1364 } 1365 1366 POP(value) 1367 1368 if (value.tag == ARRAY_TAG) { 1369 return ArrayCopy(dataPtr, &value); 1370 } 1371 1372 *dataPtr = value; 1373 return STAT_OK; 1374 } 1375 1376 /* 1377 ** copy the top value of the stack 1378 ** Before: TheStack-> value, next, ... 1379 ** After: TheStack-> value, value, next, ... 1380 */ 1381 static int dupStack(void) 1382 { 1383 DataValue value; 1384 1385 DISASM_RT(PC-1, 1); 1386 STACKDUMP(1, 3); 1387 1388 PEEK(value, 0) 1389 PUSH(value) 1390 1391 return STAT_OK; 1392 } 1393 1394 /* 1395 ** if left and right arguments are arrays, then the result is a new array 1396 ** in which all the keys from both the right and left are copied 1397 ** the values from the right array are used in the result array when the 1398 ** keys are the same 1399 ** Before: TheStack-> value2, value1, next, ... 1400 ** After: TheStack-> resValue, next, ... 1401 */ 1402 static int add(void) 1403 { 1404 DataValue leftVal, rightVal, resultArray; 1405 int n1, n2; 1406 1407 DISASM_RT(PC-1, 1); 1408 STACKDUMP(2, 3); 1409 1410 PEEK(rightVal, 0) 1411 if (rightVal.tag == ARRAY_TAG) { 1412 PEEK(leftVal, 1) 1413 if (leftVal.tag == ARRAY_TAG) { 1414 SparseArrayEntry *leftIter, *rightIter; 1415 resultArray.tag = ARRAY_TAG; 1416 resultArray.val.arrayPtr = ArrayNew(); 1417 1418 POP(rightVal) 1419 POP(leftVal) 1420 leftIter = arrayIterateFirst(&leftVal); 1421 rightIter = arrayIterateFirst(&rightVal); 1422 while (leftIter || rightIter) { 1423 Boolean insertResult = True; 1424 1425 if (leftIter && rightIter) { 1426 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter); 1427 if (compareResult < 0) { 1428 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1429 leftIter = arrayIterateNext(leftIter); 1430 } 1431 else if (compareResult > 0) { 1432 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1433 rightIter = arrayIterateNext(rightIter); 1434 } 1435 else { 1436 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1437 leftIter = arrayIterateNext(leftIter); 1438 rightIter = arrayIterateNext(rightIter); 1439 } 1440 } 1441 else if (leftIter) { 1442 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1443 leftIter = arrayIterateNext(leftIter); 1444 } 1445 else { 1446 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1447 rightIter = arrayIterateNext(rightIter); 1448 } 1449 if (!insertResult) { 1450 return(execError("array insertion failure", NULL)); 1451 } 1452 } 1453 PUSH(resultArray) 1454 } 1455 else { 1456 return(execError("can''t mix math with arrays and non-arrays", NULL)); 1457 } 1458 } 1459 else { 1460 POP_INT(n2) 1461 POP_INT(n1) 1462 PUSH_INT(n1 + n2) 1463 } 1464 return(STAT_OK); 1465 } 1466 1467 /* 1468 ** if left and right arguments are arrays, then the result is a new array 1469 ** in which only the keys which exist in the left array but not in the right 1470 ** are copied 1471 ** Before: TheStack-> value2, value1, next, ... 1472 ** After: TheStack-> resValue, next, ... 1473 */ 1474 static int subtract(void) 1475 { 1476 DataValue leftVal, rightVal, resultArray; 1477 int n1, n2; 1478 1479 DISASM_RT(PC-1, 1); 1480 STACKDUMP(2, 3); 1481 1482 PEEK(rightVal, 0) 1483 if (rightVal.tag == ARRAY_TAG) { 1484 PEEK(leftVal, 1) 1485 if (leftVal.tag == ARRAY_TAG) { 1486 SparseArrayEntry *leftIter, *rightIter; 1487 resultArray.tag = ARRAY_TAG; 1488 resultArray.val.arrayPtr = ArrayNew(); 1489 1490 POP(rightVal) 1491 POP(leftVal) 1492 leftIter = arrayIterateFirst(&leftVal); 1493 rightIter = arrayIterateFirst(&rightVal); 1494 while (leftIter) { 1495 Boolean insertResult = True; 1496 1497 if (rightIter) { 1498 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter); 1499 if (compareResult < 0) { 1500 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1501 leftIter = arrayIterateNext(leftIter); 1502 } 1503 else if (compareResult > 0) { 1504 rightIter = arrayIterateNext(rightIter); 1505 } 1506 else { 1507 leftIter = arrayIterateNext(leftIter); 1508 rightIter = arrayIterateNext(rightIter); 1509 } 1510 } 1511 else { 1512 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1513 leftIter = arrayIterateNext(leftIter); 1514 } 1515 if (!insertResult) { 1516 return(execError("array insertion failure", NULL)); 1517 } 1518 } 1519 PUSH(resultArray) 1520 } 1521 else { 1522 return(execError("can''t mix math with arrays and non-arrays", NULL)); 1523 } 1524 } 1525 else { 1526 POP_INT(n2) 1527 POP_INT(n1) 1528 PUSH_INT(n1 - n2) 1529 } 1530 return(STAT_OK); 1531 } 1532 1533 /* 1534 ** Other binary operators 1535 ** Before: TheStack-> value2, value1, next, ... 1536 ** After: TheStack-> resValue, next, ... 1537 ** 1538 ** Other unary operators 1539 ** Before: TheStack-> value, next, ... 1540 ** After: TheStack-> resValue, next, ... 1541 */ 1542 static int multiply(void) 1543 { 1544 BINARY_NUMERIC_OPERATION(*) 1545 } 1546 1547 static int divide(void) 1548 { 1549 int n1, n2; 1550 1551 DISASM_RT(PC-1, 1); 1552 STACKDUMP(2, 3); 1553 1554 POP_INT(n2) 1555 POP_INT(n1) 1556 if (n2 == 0) { 1557 return execError("division by zero", ""); 1558 } 1559 PUSH_INT(n1 / n2) 1560 return STAT_OK; 1561 } 1562 1563 static int modulo(void) 1564 { 1565 int n1, n2; 1566 1567 DISASM_RT(PC-1, 1); 1568 STACKDUMP(2, 3); 1569 1570 POP_INT(n2) 1571 POP_INT(n1) 1572 if (n2 == 0) { 1573 return execError("modulo by zero", ""); 1574 } 1575 PUSH_INT(n1 % n2) 1576 return STAT_OK; 1577 } 1578 1579 static int negate(void) 1580 { 1581 UNARY_NUMERIC_OPERATION(-) 1582 } 1583 1584 static int increment(void) 1585 { 1586 UNARY_NUMERIC_OPERATION(++) 1587 } 1588 1589 static int decrement(void) 1590 { 1591 UNARY_NUMERIC_OPERATION(--) 1592 } 1593 1594 static int gt(void) 1595 { 1596 BINARY_NUMERIC_OPERATION(>) 1597 } 1598 1599 static int lt(void) 1600 { 1601 BINARY_NUMERIC_OPERATION(<) 1602 } 1603 1604 static int ge(void) 1605 { 1606 BINARY_NUMERIC_OPERATION(>=) 1607 } 1608 1609 static int le(void) 1610 { 1611 BINARY_NUMERIC_OPERATION(<=) 1612 } 1613 1614 /* 1615 ** verify that compares are between integers and/or strings only 1616 ** Before: TheStack-> value1, value2, next, ... 1617 ** After: TheStack-> resValue, next, ... 1618 ** where resValue is 1 for true, 0 for false 1619 */ 1620 static int eq(void) 1621 { 1622 DataValue v1, v2; 1623 1624 DISASM_RT(PC-1, 1); 1625 STACKDUMP(2, 3); 1626 1627 POP(v1) 1628 POP(v2) 1629 if (v1.tag == INT_TAG && v2.tag == INT_TAG) { 1630 v1.val.n = v1.val.n == v2.val.n; 1631 } 1632 else if (v1.tag == STRING_TAG && v2.tag == STRING_TAG) { 1633 v1.val.n = !strcmp(v1.val.str.rep, v2.val.str.rep); 1634 } 1635 else if (v1.tag == STRING_TAG && v2.tag == INT_TAG) { 1636 int number; 1637 if (!StringToNum(v1.val.str.rep, &number)) { 1638 v1.val.n = 0; 1639 } 1640 else { 1641 v1.val.n = number == v2.val.n; 1642 } 1643 } 1644 else if (v2.tag == STRING_TAG && v1.tag == INT_TAG) { 1645 int number; 1646 if (!StringToNum(v2.val.str.rep, &number)) { 1647 v1.val.n = 0; 1648 } 1649 else { 1650 v1.val.n = number == v1.val.n; 1651 } 1652 } 1653 else { 1654 return(execError("incompatible types to compare", NULL)); 1655 } 1656 v1.tag = INT_TAG; 1657 PUSH(v1) 1658 return(STAT_OK); 1659 } 1660 1661 /* negated eq() call */ 1662 static int ne(void) 1663 { 1664 eq(); 1665 return not(); 1666 } 1667 1668 /* 1669 ** if left and right arguments are arrays, then the result is a new array 1670 ** in which only the keys which exist in both the right or left are copied 1671 ** the values from the right array are used in the result array 1672 ** Before: TheStack-> value2, value1, next, ... 1673 ** After: TheStack-> resValue, next, ... 1674 */ 1675 static int bitAnd(void) 1676 { 1677 DataValue leftVal, rightVal, resultArray; 1678 int n1, n2; 1679 1680 DISASM_RT(PC-1, 1); 1681 STACKDUMP(2, 3); 1682 1683 PEEK(rightVal, 0) 1684 if (rightVal.tag == ARRAY_TAG) { 1685 PEEK(leftVal, 1) 1686 if (leftVal.tag == ARRAY_TAG) { 1687 SparseArrayEntry *leftIter, *rightIter; 1688 resultArray.tag = ARRAY_TAG; 1689 resultArray.val.arrayPtr = ArrayNew(); 1690 1691 POP(rightVal) 1692 POP(leftVal) 1693 leftIter = arrayIterateFirst(&leftVal); 1694 rightIter = arrayIterateFirst(&rightVal); 1695 while (leftIter && rightIter) { 1696 Boolean insertResult = True; 1697 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter); 1698 1699 if (compareResult < 0) { 1700 leftIter = arrayIterateNext(leftIter); 1701 } 1702 else if (compareResult > 0) { 1703 rightIter = arrayIterateNext(rightIter); 1704 } 1705 else { 1706 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1707 leftIter = arrayIterateNext(leftIter); 1708 rightIter = arrayIterateNext(rightIter); 1709 } 1710 if (!insertResult) { 1711 return(execError("array insertion failure", NULL)); 1712 } 1713 } 1714 PUSH(resultArray) 1715 } 1716 else { 1717 return(execError("can''t mix math with arrays and non-arrays", NULL)); 1718 } 1719 } 1720 else { 1721 POP_INT(n2) 1722 POP_INT(n1) 1723 PUSH_INT(n1 & n2) 1724 } 1725 return(STAT_OK); 1726 } 1727 1728 /* 1729 ** if left and right arguments are arrays, then the result is a new array 1730 ** in which only the keys which exist in either the right or left but not both 1731 ** are copied 1732 ** Before: TheStack-> value2, value1, next, ... 1733 ** After: TheStack-> resValue, next, ... 1734 */ 1735 static int bitOr(void) 1736 { 1737 DataValue leftVal, rightVal, resultArray; 1738 int n1, n2; 1739 1740 DISASM_RT(PC-1, 1); 1741 STACKDUMP(2, 3); 1742 1743 PEEK(rightVal, 0) 1744 if (rightVal.tag == ARRAY_TAG) { 1745 PEEK(leftVal, 1) 1746 if (leftVal.tag == ARRAY_TAG) { 1747 SparseArrayEntry *leftIter, *rightIter; 1748 resultArray.tag = ARRAY_TAG; 1749 resultArray.val.arrayPtr = ArrayNew(); 1750 1751 POP(rightVal) 1752 POP(leftVal) 1753 leftIter = arrayIterateFirst(&leftVal); 1754 rightIter = arrayIterateFirst(&rightVal); 1755 while (leftIter || rightIter) { 1756 Boolean insertResult = True; 1757 1758 if (leftIter && rightIter) { 1759 int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter); 1760 if (compareResult < 0) { 1761 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1762 leftIter = arrayIterateNext(leftIter); 1763 } 1764 else if (compareResult > 0) { 1765 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1766 rightIter = arrayIterateNext(rightIter); 1767 } 1768 else { 1769 leftIter = arrayIterateNext(leftIter); 1770 rightIter = arrayIterateNext(rightIter); 1771 } 1772 } 1773 else if (leftIter) { 1774 insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value); 1775 leftIter = arrayIterateNext(leftIter); 1776 } 1777 else { 1778 insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value); 1779 rightIter = arrayIterateNext(rightIter); 1780 } 1781 if (!insertResult) { 1782 return(execError("array insertion failure", NULL)); 1783 } 1784 } 1785 PUSH(resultArray) 1786 } 1787 else { 1788 return(execError("can''t mix math with arrays and non-arrays", NULL)); 1789 } 1790 } 1791 else { 1792 POP_INT(n2) 1793 POP_INT(n1) 1794 PUSH_INT(n1 | n2) 1795 } 1796 return(STAT_OK); 1797 } 1798 1799 static int and(void) 1800 { 1801 BINARY_NUMERIC_OPERATION(&&) 1802 } 1803 1804 static int or(void) 1805 { 1806 BINARY_NUMERIC_OPERATION(||) 1807 } 1808 1809 static int not(void) 1810 { 1811 UNARY_NUMERIC_OPERATION(!) 1812 } 1813 1814 /* 1815 ** raise one number to the power of another 1816 ** Before: TheStack-> raisedBy, number, next, ... 1817 ** After: TheStack-> result, next, ... 1818 */ 1819 static int power(void) 1820 { 1821 int n1, n2, n3; 1822 1823 DISASM_RT(PC-1, 1); 1824 STACKDUMP(2, 3); 1825 1826 POP_INT(n2) 1827 POP_INT(n1) 1828 /* We need to round to deal with pow() giving results slightly above 1829 or below the real result since it deals with floating point numbers. 1830 Note: We're not really wanting rounded results, we merely 1831 want to deal with this simple issue. So, 2^-2 = .5, but we 1832 don't want to round this to 1. This is mainly intended to deal with 1833 4^2 = 15.999996 and 16.000001. 1834 */ 1835 if (n2 < 0 && n1 != 1 && n1 != -1) { 1836 if (n1 != 0) { 1837 /* since we're integer only, nearly all negative exponents result in 0 */ 1838 n3 = 0; 1839 } 1840 else { 1841 /* allow error to occur */ 1842 n3 = (int)pow((double)n1, (double)n2); 1843 } 1844 } 1845 else { 1846 if ((n1 < 0) && (n2 & 1)) { 1847 /* round to nearest integer for negative values*/ 1848 n3 = (int)(pow((double)n1, (double)n2) - (double)0.5); 1849 } 1850 else { 1851 /* round to nearest integer for positive values*/ 1852 n3 = (int)(pow((double)n1, (double)n2) + (double)0.5); 1853 } 1854 } 1855 PUSH_INT(n3) 1856 return errCheck("exponentiation"); 1857 } 1858 1859 /* 1860 ** concatenate two top items on the stack 1861 ** Before: TheStack-> str2, str1, next, ... 1862 ** After: TheStack-> result, next, ... 1863 */ 1864 static int concat(void) 1865 { 1866 char *s1, *s2, *out; 1867 int len1, len2; 1868 1869 DISASM_RT(PC-1, 1); 1870 STACKDUMP(2, 3); 1871 1872 POP_STRING(s2) 1873 POP_STRING(s1) 1874 len1 = strlen(s1); 1875 len2 = strlen(s2); 1876 out = AllocString(len1 + len2 + 1); 1877 strncpy(out, s1, len1); 1878 strcpy(&out[len1], s2); 1879 PUSH_STRING(out, len1 + len2) 1880 return STAT_OK; 1881 } 1882 1883 /* 1884 ** Call a subroutine or function (user defined or built-in). Args are the 1885 ** subroutine's symbol, and the number of arguments which have been pushed 1886 ** on the stack. 1887 ** 1888 ** For a macro subroutine, the return address, frame pointer, number of 1889 ** arguments and space for local variables are added to the stack, and the 1890 ** PC is set to point to the new function. For a built-in routine, the 1891 ** arguments are popped off the stack, and the routine is just called. 1892 ** 1893 ** Before: Prog-> [subrSym], nArgs, next, ... 1894 ** TheStack-> argN-arg1, next, ... 1895 ** After: Prog-> next, ... -- (built-in called subr) 1896 ** TheStack-> retVal?, next, ... 1897 ** or: Prog-> (in called)next, ... -- (macro code called subr) 1898 ** TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... 1899 */ 1900 static int callSubroutine(void) 1901 { 1902 Symbol *sym, *s; 1903 int i, nArgs; 1904 static DataValue noValue = {NO_TAG, {0}}; 1905 Program *prog; 1906 char *errMsg; 1907 1908 sym = PC->sym; 1909 PC++; 1910 nArgs = PC->value; 1911 PC++; 1912 1913 DISASM_RT(PC-3, 3); 1914 STACKDUMP(nArgs, 3); 1915 1916 /* 1917 ** If the subroutine is built-in, call the built-in routine 1918 */ 1919 if (sym->type == C_FUNCTION_SYM) { 1920 DataValue result; 1921 1922 /* "pop" stack back to the first argument in the call stack */ 1923 StackP -= nArgs; 1924 1925 /* Call the function and check for preemption */ 1926 PreemptRequest = False; 1927 if (!sym->value.val.subr(FocusWindow, StackP, 1928 nArgs, &result, &errMsg)) 1929 return execError(errMsg, sym->name); 1930 if (PC->func == fetchRetVal) { 1931 if (result.tag == NO_TAG) { 1932 return execError("%s does not return a value", sym->name); 1933 } 1934 PUSH(result); 1935 PC++; 1936 } 1937 return PreemptRequest ? STAT_PREEMPT : STAT_OK; 1938 } 1939 1940 /* 1941 ** Call a macro subroutine: 1942 ** 1943 ** Push all of the required information to resume, and make space on the 1944 ** stack for local variables (and initialize them), on top of the argument 1945 ** values which are already there. 1946 */ 1947 if (sym->type == MACRO_FUNCTION_SYM) { 1948 StackP->tag = NO_TAG; /* return PC */ 1949 StackP->val.inst = PC; 1950 StackP++; 1951 1952 StackP->tag = NO_TAG; /* old FrameP */ 1953 StackP->val.dataval = FrameP; 1954 StackP++; 1955 1956 StackP->tag = NO_TAG; /* nArgs */ 1957 StackP->val.n = nArgs; 1958 StackP++; 1959 1960 *(StackP++) = noValue; /* cached arg array */ 1961 1962 FrameP = StackP; 1963 prog = sym->value.val.prog; 1964 PC = prog->code; 1965 for (s = prog->localSymList; s != NULL; s = s->next) { 1966 FP_GET_SYM_VAL(FrameP, s) = noValue; 1967 StackP++; 1968 } 1969 return STAT_OK; 1970 } 1971 1972 /* 1973 ** Call an action routine 1974 */ 1975 if (sym->type == ACTION_ROUTINE_SYM) { 1976 String *argList; 1977 Cardinal numArgs = nArgs; 1978 XKeyEvent key_event; 1979 Display *disp; 1980 Window win; 1981 1982 /* Create a fake event with a timestamp suitable for actions which need 1983 timestamps, a marker to indicate that the call was from a macro 1984 (to stop shell commands from putting up their own separate banner) */ 1985 disp=XtDisplay(InitiatingWindow->shell); 1986 win=XtWindow(InitiatingWindow->shell); 1987 1988 key_event.type = KeyPress; 1989 key_event.send_event = MACRO_EVENT_MARKER; 1990 key_event.time=XtLastTimestampProcessed(XtDisplay(InitiatingWindow->shell)); 1991 1992 /* The following entries are just filled in to avoid problems 1993 in strange cases, like calling "self_insert()" directly from the 1994 macro menu. In fact the display was sufficient to cure this crash. */ 1995 key_event.display=disp; 1996 key_event.window=key_event.root=key_event.subwindow=win; 1997 1998 argList = (String *)NEditCalloc(nArgs, sizeof(*argList)); 1999 /* pop arguments off the stack and put them in the argument list */ 2000 for (i=nArgs-1; i>=0; i--) { 2001 POP_STRING(argList[i]) 2002 } 2003 2004 /* Call the action routine and check for preemption */ 2005 PreemptRequest = False; 2006 sym->value.val.xtproc(FocusWindow->lastFocus, 2007 (XEvent *)&key_event, argList, &numArgs); 2008 NEditFree(argList); 2009 if (PC->func == fetchRetVal) { 2010 return execError("%s does not return a value", sym->name); 2011 } 2012 return PreemptRequest ? STAT_PREEMPT : STAT_OK; 2013 } 2014 2015 /* Calling a non subroutine symbol */ 2016 return execError("%s is not a function or subroutine", sym->name); 2017 } 2018 2019 /* 2020 ** This should never be executed, returnVal checks for the presence of this 2021 ** instruction at the PC to decide whether to push the function's return 2022 ** value, then skips over it without executing. 2023 */ 2024 static int fetchRetVal(void) 2025 { 2026 return execError("internal error: frv", NULL); 2027 } 2028 2029 /* see comments for returnValOrNone() */ 2030 static int returnNoVal(void) 2031 { 2032 return returnValOrNone(False); 2033 } 2034 static int returnVal(void) 2035 { 2036 return returnValOrNone(True); 2037 } 2038 2039 /* 2040 ** Return from a subroutine call 2041 ** Before: Prog-> [next], ... 2042 ** TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... 2043 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ... 2044 ** TheStack-> retVal?, next, ... 2045 */ 2046 static int returnValOrNone(int valOnStack) 2047 { 2048 DataValue retVal; 2049 static DataValue noValue = {NO_TAG, {0}}; 2050 DataValue *newFrameP; 2051 int nArgs; 2052 2053 DISASM_RT(PC-1, 1); 2054 STACKDUMP(StackP - FrameP + FP_GET_ARG_COUNT(FrameP) + FP_TO_ARGS_DIST, 3); 2055 2056 /* return value is on the stack */ 2057 if (valOnStack) { 2058 POP(retVal); 2059 } 2060 2061 /* get stored return information */ 2062 nArgs = FP_GET_ARG_COUNT(FrameP); 2063 newFrameP = FP_GET_OLD_FP(FrameP); 2064 PC = FP_GET_RET_PC(FrameP); 2065 2066 /* pop past local variables */ 2067 StackP = FrameP; 2068 /* pop past function arguments */ 2069 StackP -= (FP_TO_ARGS_DIST + nArgs); 2070 FrameP = newFrameP; 2071 2072 /* push returned value, if requsted */ 2073 if (PC == NULL) { 2074 if (valOnStack) { 2075 PUSH(retVal); 2076 } else { 2077 PUSH(noValue); 2078 } 2079 } else if (PC->func == fetchRetVal) { 2080 if (valOnStack) { 2081 PUSH(retVal); 2082 PC++; 2083 } else { 2084 return execError( 2085 "using return value of %s which does not return a value", 2086 ((PC-2)->sym->name)); 2087 } 2088 } 2089 2090 /* NULL return PC indicates end of program */ 2091 return PC == NULL ? STAT_DONE : STAT_OK; 2092 } 2093 2094 /* 2095 ** Unconditional branch offset by immediate operand 2096 ** 2097 ** Before: Prog-> [branchDest], next, ..., (branchdest)next 2098 ** After: Prog-> branchDest, next, ..., (branchdest)[next] 2099 */ 2100 static int branch(void) 2101 { 2102 DISASM_RT(PC-1, 2); 2103 STACKDUMP(0, 3); 2104 2105 PC += PC->value; 2106 return STAT_OK; 2107 } 2108 2109 /* 2110 ** Conditional branches if stack value is True/False (non-zero/0) to address 2111 ** of immediate operand (pops stack) 2112 ** 2113 ** Before: Prog-> [branchDest], next, ..., (branchdest)next 2114 ** After: either: Prog-> branchDest, [next], ... 2115 ** After: or: Prog-> branchDest, next, ..., (branchdest)[next] 2116 */ 2117 static int branchTrue(void) 2118 { 2119 int value; 2120 Inst *addr; 2121 2122 DISASM_RT(PC-1, 2); 2123 STACKDUMP(1, 3); 2124 2125 POP_INT(value) 2126 addr = PC + PC->value; 2127 PC++; 2128 2129 if (value) 2130 PC = addr; 2131 return STAT_OK; 2132 } 2133 static int branchFalse(void) 2134 { 2135 int value; 2136 Inst *addr; 2137 2138 DISASM_RT(PC-1, 2); 2139 STACKDUMP(1, 3); 2140 2141 POP_INT(value) 2142 addr = PC + PC->value; 2143 PC++; 2144 2145 if (!value) 2146 PC = addr; 2147 return STAT_OK; 2148 } 2149 2150 /* 2151 ** Ignore the address following the instruction and continue. Why? So 2152 ** some code that uses conditional branching doesn't have to figure out 2153 ** whether to store a branch address. 2154 ** 2155 ** Before: Prog-> [branchDest], next, ... 2156 ** After: Prog-> branchDest, [next], ... 2157 */ 2158 static int branchNever(void) 2159 { 2160 DISASM_RT(PC-1, 2); 2161 STACKDUMP(0, 3); 2162 2163 PC++; 2164 return STAT_OK; 2165 } 2166 2167 /* 2168 ** recursively copy(duplicate) the sparse array nodes of an array 2169 ** this does not duplicate the key/node data since they are never 2170 ** modified, only replaced 2171 */ 2172 int ArrayCopy(DataValue *dstArray, DataValue *srcArray) 2173 { 2174 SparseArrayEntry *srcIter; 2175 2176 dstArray->tag = ARRAY_TAG; 2177 dstArray->val.arrayPtr = ArrayNew(); 2178 2179 srcIter = arrayIterateFirst(srcArray); 2180 while (srcIter) { 2181 if (srcIter->value.tag == ARRAY_TAG) { 2182 int errNum; 2183 DataValue tmpArray; 2184 2185 errNum = ArrayCopy(&tmpArray, &srcIter->value); 2186 if (errNum != STAT_OK) { 2187 return(errNum); 2188 } 2189 if (!ArrayInsert(dstArray, srcIter->key, &tmpArray)) { 2190 return(execError("array copy failed", NULL)); 2191 } 2192 } 2193 else { 2194 if (!ArrayInsert(dstArray, srcIter->key, &srcIter->value)) { 2195 return(execError("array copy failed", NULL)); 2196 } 2197 } 2198 srcIter = arrayIterateNext(srcIter); 2199 } 2200 return(STAT_OK); 2201 } 2202 2203 /* 2204 ** creates an allocated string of a single key for all the sub-scripts 2205 ** using ARRAY_DIM_SEP as a separator 2206 ** this function uses the PEEK macros in order to remove most limits on 2207 ** the number of arguments to an array 2208 ** I really need to optimize the size approximation rather than assuming 2209 ** a worst case size for every integer argument 2210 */ 2211 static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams) 2212 { 2213 DataValue tmpVal; 2214 int sepLen = strlen(ARRAY_DIM_SEP); 2215 int keyLength = 0; 2216 int i; 2217 2218 keyLength = sepLen * (nArgs - 1); 2219 for (i = nArgs - 1; i >= 0; --i) { 2220 PEEK(tmpVal, i) 2221 if (tmpVal.tag == INT_TAG) { 2222 keyLength += TYPE_INT_STR_SIZE(tmpVal.val.n); 2223 } 2224 else if (tmpVal.tag == STRING_TAG) { 2225 keyLength += tmpVal.val.str.len; 2226 } 2227 else { 2228 return(execError("can only index array with string or int.", NULL)); 2229 } 2230 } 2231 *keyString = AllocString(keyLength + 1); 2232 (*keyString)[0] = 0; 2233 for (i = nArgs - 1; i >= 0; --i) { 2234 if (i != nArgs - 1) { 2235 strcat(*keyString, ARRAY_DIM_SEP); 2236 } 2237 PEEK(tmpVal, i) 2238 if (tmpVal.tag == INT_TAG) { 2239 sprintf(&((*keyString)[strlen(*keyString)]), "%d", tmpVal.val.n); 2240 } 2241 else if (tmpVal.tag == STRING_TAG) { 2242 strcat(*keyString, tmpVal.val.str.rep); 2243 } 2244 else { 2245 return(execError("can only index array with string or int.", NULL)); 2246 } 2247 } 2248 if (!leaveParams) { 2249 for (i = nArgs - 1; i >= 0; --i) { 2250 POP(tmpVal) 2251 } 2252 } 2253 return(STAT_OK); 2254 } 2255 2256 /* 2257 ** allocate an empty array node, this is used as the root node and never 2258 ** contains any data, only refernces to other nodes 2259 */ 2260 static rbTreeNode *arrayEmptyAllocator(void) 2261 { 2262 SparseArrayEntry *newNode = allocateSparseArrayEntry(); 2263 if (newNode) { 2264 newNode->key = NULL; 2265 newNode->value.tag = NO_TAG; 2266 } 2267 return((rbTreeNode *)newNode); 2268 } 2269 2270 /* 2271 ** create and copy array node and copy contents, we merely copy pointers 2272 ** since they are never modified, only replaced 2273 */ 2274 static rbTreeNode *arrayAllocateNode(rbTreeNode *src) 2275 { 2276 SparseArrayEntry *newNode = allocateSparseArrayEntry(); 2277 if (newNode) { 2278 newNode->key = ((SparseArrayEntry *)src)->key; 2279 newNode->value = ((SparseArrayEntry *)src)->value; 2280 } 2281 return((rbTreeNode *)newNode); 2282 } 2283 2284 /* 2285 ** copy array node data, we merely copy pointers since they are never 2286 ** modified, only replaced 2287 */ 2288 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src) 2289 { 2290 ((SparseArrayEntry *)dst)->key = ((SparseArrayEntry *)src)->key; 2291 ((SparseArrayEntry *)dst)->value = ((SparseArrayEntry *)src)->value; 2292 return(1); 2293 } 2294 2295 /* 2296 ** compare two array nodes returning an integer value similar to strcmp() 2297 */ 2298 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right) 2299 { 2300 return(strcmp(((SparseArrayEntry *)left)->key, ((SparseArrayEntry *)right)->key)); 2301 } 2302 2303 /* 2304 ** dispose an array node, garbage collection handles this, so we mark it 2305 ** to allow iterators in macro language to determine they have been unlinked 2306 */ 2307 static void arrayDisposeNode(rbTreeNode *src) 2308 { 2309 /* Let garbage collection handle this but mark it so iterators can tell */ 2310 src->left = NULL; 2311 src->right = NULL; 2312 src->parent = NULL; 2313 src->color = -1; 2314 } 2315 2316 SparseArrayEntry *ArrayNew(void) 2317 { 2318 return((SparseArrayEntry *)rbTreeNew(arrayEmptyAllocator)); 2319 } 2320 2321 /* 2322 ** insert a DataValue into an array, allocate the array if needed 2323 ** keyStr must be a string that was allocated with AllocString() 2324 */ 2325 Boolean ArrayInsert(DataValue* theArray, char* keyStr, DataValue* theValue) 2326 { 2327 SparseArrayEntry tmpEntry; 2328 rbTreeNode *insertedNode; 2329 2330 tmpEntry.key = keyStr; 2331 tmpEntry.value = *theValue; 2332 2333 if (theArray->val.arrayPtr == NULL) { 2334 theArray->val.arrayPtr = ArrayNew(); 2335 } 2336 2337 if (theArray->val.arrayPtr != NULL) { 2338 insertedNode = rbTreeInsert((rbTreeNode*) (theArray->val.arrayPtr), 2339 (rbTreeNode *)&tmpEntry, arrayEntryCompare, arrayAllocateNode, 2340 arrayEntryCopyToNode); 2341 2342 if (insertedNode) { 2343 return True; 2344 } else { 2345 return False; 2346 } 2347 } 2348 2349 return False; 2350 } 2351 2352 /* 2353 ** remove a node from an array whose key matches keyStr 2354 */ 2355 void ArrayDelete(DataValue *theArray, char *keyStr) 2356 { 2357 SparseArrayEntry searchEntry; 2358 2359 if (theArray->val.arrayPtr) { 2360 searchEntry.key = keyStr; 2361 rbTreeDelete((rbTreeNode *)theArray->val.arrayPtr, (rbTreeNode *)&searchEntry, 2362 arrayEntryCompare, arrayDisposeNode); 2363 } 2364 } 2365 2366 /* 2367 ** remove all nodes from an array 2368 */ 2369 void ArrayDeleteAll(DataValue *theArray) 2370 { 2371 if (theArray->val.arrayPtr) { 2372 rbTreeNode *iter = rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr); 2373 while (iter) { 2374 rbTreeNode *nextIter = rbTreeNext(iter); 2375 rbTreeDeleteNode((rbTreeNode *)theArray->val.arrayPtr, 2376 iter, arrayDisposeNode); 2377 2378 iter = nextIter; 2379 } 2380 } 2381 } 2382 2383 /* 2384 ** returns the number of elements (nodes containing values) of an array 2385 */ 2386 unsigned ArraySize(DataValue* theArray) 2387 { 2388 if (theArray->val.arrayPtr) { 2389 return rbTreeSize((rbTreeNode *)theArray->val.arrayPtr); 2390 } else { 2391 return 0; 2392 } 2393 } 2394 2395 /* 2396 ** retrieves an array node whose key matches 2397 ** returns 1 for success 0 for not found 2398 */ 2399 Boolean ArrayGet(DataValue* theArray, char* keyStr, DataValue* theValue) 2400 { 2401 SparseArrayEntry searchEntry; 2402 rbTreeNode *foundNode; 2403 2404 if (theArray->val.arrayPtr) { 2405 searchEntry.key = keyStr; 2406 foundNode = rbTreeFind((rbTreeNode*) theArray->val.arrayPtr, 2407 (rbTreeNode*) &searchEntry, arrayEntryCompare); 2408 if (foundNode) { 2409 *theValue = ((SparseArrayEntry*) foundNode)->value; 2410 return True; 2411 } 2412 } 2413 2414 return False; 2415 } 2416 2417 /* 2418 ** get pointer to start iterating an array 2419 */ 2420 SparseArrayEntry *arrayIterateFirst(DataValue *theArray) 2421 { 2422 SparseArrayEntry *startPos; 2423 if (theArray->val.arrayPtr) { 2424 startPos = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr); 2425 } 2426 else { 2427 startPos = NULL; 2428 } 2429 return(startPos); 2430 } 2431 2432 /* 2433 ** move iterator to next entry in array 2434 */ 2435 SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator) 2436 { 2437 SparseArrayEntry *nextPos; 2438 if (iterator) { 2439 nextPos = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)iterator); 2440 } 2441 else { 2442 nextPos = NULL; 2443 } 2444 return(nextPos); 2445 } 2446 2447 /* 2448 ** evaluate an array element and push the result onto the stack 2449 ** 2450 ** Before: Prog-> [nDim], next, ... 2451 ** TheStack-> indnDim, ... ind1, ArraySym, next, ... 2452 ** After: Prog-> nDim, [next], ... 2453 ** TheStack-> indexedArrayVal, next, ... 2454 */ 2455 static int arrayRef(void) 2456 { 2457 int errNum; 2458 DataValue srcArray, valueItem; 2459 char *keyString = NULL; 2460 int nDim; 2461 2462 nDim = PC->value; 2463 PC++; 2464 2465 DISASM_RT(PC-2, 2); 2466 STACKDUMP(nDim, 3); 2467 2468 if (nDim > 0) { 2469 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0); 2470 if (errNum != STAT_OK) { 2471 return(errNum); 2472 } 2473 2474 POP(srcArray) 2475 if (srcArray.tag == ARRAY_TAG) { 2476 if (!ArrayGet(&srcArray, keyString, &valueItem)) { 2477 return(execError("referenced array value not in array: %s", keyString)); 2478 } 2479 PUSH(valueItem) 2480 return(STAT_OK); 2481 } 2482 else { 2483 return(execError("operator [] on non-array", NULL)); 2484 } 2485 } 2486 else { 2487 POP(srcArray) 2488 if (srcArray.tag == ARRAY_TAG) { 2489 PUSH_INT(ArraySize(&srcArray)) 2490 return(STAT_OK); 2491 } 2492 else { 2493 return(execError("operator [] on non-array", NULL)); 2494 } 2495 } 2496 } 2497 2498 /* 2499 ** assign to an array element of a referenced array on the stack 2500 ** 2501 ** Before: Prog-> [nDim], next, ... 2502 ** TheStack-> rhs, indnDim, ... ind1, ArraySym, next, ... 2503 ** After: Prog-> nDim, [next], ... 2504 ** TheStack-> next, ... 2505 */ 2506 static int arrayAssign(void) 2507 { 2508 char *keyString = NULL; 2509 DataValue srcValue, dstArray; 2510 int errNum; 2511 int nDim; 2512 2513 nDim = PC->value; 2514 PC++; 2515 2516 DISASM_RT(PC-2, 1); 2517 STACKDUMP(nDim, 3); 2518 2519 if (nDim > 0) { 2520 POP(srcValue) 2521 2522 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0); 2523 if (errNum != STAT_OK) { 2524 return(errNum); 2525 } 2526 2527 POP(dstArray) 2528 2529 if (dstArray.tag != ARRAY_TAG && dstArray.tag != NO_TAG) { 2530 return(execError("cannot assign array element of non-array", NULL)); 2531 } 2532 if (srcValue.tag == ARRAY_TAG) { 2533 DataValue arrayCopyValue; 2534 2535 errNum = ArrayCopy(&arrayCopyValue, &srcValue); 2536 srcValue = arrayCopyValue; 2537 if (errNum != STAT_OK) { 2538 return(errNum); 2539 } 2540 } 2541 if (ArrayInsert(&dstArray, keyString, &srcValue)) { 2542 return(STAT_OK); 2543 } 2544 else { 2545 return(execError("array member allocation failure", NULL)); 2546 } 2547 } 2548 return(execError("empty operator []", NULL)); 2549 } 2550 2551 /* 2552 ** for use with assign-op operators (eg a[i,j] += k 2553 ** 2554 ** Before: Prog-> [binOp], nDim, next, ... 2555 ** TheStack-> [rhs], indnDim, ... ind1, next, ... 2556 ** After: Prog-> binOp, nDim, [next], ... 2557 ** TheStack-> [rhs], arrayValue, next, ... 2558 */ 2559 static int arrayRefAndAssignSetup(void) 2560 { 2561 int errNum; 2562 DataValue srcArray, valueItem, moveExpr; 2563 char *keyString = NULL; 2564 int binaryOp, nDim; 2565 2566 binaryOp = PC->value; 2567 PC++; 2568 nDim = PC->value; 2569 PC++; 2570 2571 DISASM_RT(PC-3, 3); 2572 STACKDUMP(nDim + 1, 3); 2573 2574 if (binaryOp) { 2575 POP(moveExpr) 2576 } 2577 2578 if (nDim > 0) { 2579 errNum = makeArrayKeyFromArgs(nDim, &keyString, 1); 2580 if (errNum != STAT_OK) { 2581 return(errNum); 2582 } 2583 2584 PEEK(srcArray, nDim) 2585 if (srcArray.tag == ARRAY_TAG) { 2586 if (!ArrayGet(&srcArray, keyString, &valueItem)) { 2587 return(execError("referenced array value not in array: %s", keyString)); 2588 } 2589 PUSH(valueItem) 2590 if (binaryOp) { 2591 PUSH(moveExpr) 2592 } 2593 return(STAT_OK); 2594 } 2595 else { 2596 return(execError("operator [] on non-array", NULL)); 2597 } 2598 } 2599 else { 2600 return(execError("array[] not an lvalue", NULL)); 2601 } 2602 } 2603 2604 /* 2605 ** setup symbol values for array iteration in interpreter 2606 ** 2607 ** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ... 2608 ** TheStack-> [arrayVal], next, ... 2609 ** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ... 2610 ** TheStack-> [next], ... 2611 ** Where: 2612 ** iter is a symbol which gives the position of the iterator value in 2613 ** the stack frame 2614 ** arrayVal is the data value holding the array in question 2615 */ 2616 static int beginArrayIter(void) 2617 { 2618 Symbol *iterator; 2619 DataValue *iteratorValPtr; 2620 DataValue arrayVal; 2621 2622 DISASM_RT(PC-1, 2); 2623 STACKDUMP(1, 3); 2624 2625 iterator = PC->sym; 2626 PC++; 2627 2628 POP(arrayVal) 2629 2630 if (iterator->type == LOCAL_SYM) { 2631 iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator); 2632 } 2633 else { 2634 return(execError("bad temporary iterator: %s", iterator->name)); 2635 } 2636 2637 iteratorValPtr->tag = INT_TAG; 2638 if (arrayVal.tag != ARRAY_TAG) { 2639 return(execError("can''t iterate non-array", NULL)); 2640 } 2641 2642 iteratorValPtr->val.arrayPtr = arrayIterateFirst(&arrayVal); 2643 return(STAT_OK); 2644 } 2645 2646 /* 2647 ** copy key to symbol if node is still valid, marked bad by a color of -1 2648 ** then move iterator to next node 2649 ** this allows iterators to progress even if you delete any node in the array 2650 ** except the item just after the current key 2651 ** 2652 ** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ... 2653 ** TheStack-> [next], ... 2654 ** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ... 2655 ** TheStack-> [next], ... (unchanged) 2656 ** Where: 2657 ** iter is a symbol which gives the position of the iterator value in 2658 ** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers 2659 ** to the array and a position within it 2660 ** iterVal is the programmer-visible symbol which will take the current 2661 ** key value 2662 ** endLoopBranch is the instruction offset to the instruction following the 2663 ** loop (measured from itself) 2664 ** arrayVal is the data value holding the array in question 2665 ** The return-to-start-of-loop branch (at the end of the loop) should address 2666 ** the ARRAY_ITER instruction 2667 */ 2668 static int arrayIter(void) 2669 { 2670 Symbol *iterator; 2671 Symbol *item; 2672 DataValue *iteratorValPtr; 2673 DataValue *itemValPtr; 2674 SparseArrayEntry *thisEntry; 2675 Inst *branchAddr; 2676 2677 DISASM_RT(PC-1, 4); 2678 STACKDUMP(0, 3); 2679 2680 item = PC->sym; 2681 PC++; 2682 iterator = PC->sym; 2683 PC++; 2684 branchAddr = PC + PC->value; 2685 PC++; 2686 2687 if (item->type == LOCAL_SYM) { 2688 itemValPtr = &FP_GET_SYM_VAL(FrameP, item); 2689 } 2690 else if (item->type == GLOBAL_SYM) { 2691 itemValPtr = &(item->value); 2692 } 2693 else { 2694 return(execError("can''t assign to: %s", item->name)); 2695 } 2696 itemValPtr->tag = NO_TAG; 2697 2698 if (iterator->type == LOCAL_SYM) { 2699 iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator); 2700 } 2701 else { 2702 return(execError("bad temporary iterator: %s", iterator->name)); 2703 } 2704 2705 thisEntry = iteratorValPtr->val.arrayPtr; 2706 if (thisEntry && thisEntry->nodePtrs.color != -1) { 2707 itemValPtr->tag = STRING_TAG; 2708 itemValPtr->val.str.rep = thisEntry->key; 2709 itemValPtr->val.str.len = strlen(thisEntry->key); 2710 2711 iteratorValPtr->val.arrayPtr = arrayIterateNext(thisEntry); 2712 } 2713 else { 2714 PC = branchAddr; 2715 } 2716 return(STAT_OK); 2717 } 2718 2719 /* 2720 ** determine if a key or keys exists in an array 2721 ** if the left argument is a string or integer a single check is performed 2722 ** if the key exists, 1 is pushed onto the stack, otherwise 0 2723 ** if the left argument is an array 1 is pushed onto the stack if every key 2724 ** in the left array exists in the right array, otherwise 0 2725 ** 2726 ** Before: Prog-> [next], ... 2727 ** TheStack-> [ArraySym], inSymbol, next, ... 2728 ** After: Prog-> [next], ... -- (unchanged) 2729 ** TheStack-> next, ... 2730 */ 2731 static int inArray(void) 2732 { 2733 DataValue theArray, leftArray, theValue; 2734 char *keyStr; 2735 int inResult = 0; 2736 2737 DISASM_RT(PC-1, 1); 2738 STACKDUMP(2, 3); 2739 2740 POP(theArray) 2741 if (theArray.tag != ARRAY_TAG) { 2742 return(execError("operator in on non-array", NULL)); 2743 } 2744 PEEK(leftArray, 0) 2745 if (leftArray.tag == ARRAY_TAG) { 2746 SparseArrayEntry *iter; 2747 2748 POP(leftArray) 2749 inResult = 1; 2750 iter = arrayIterateFirst(&leftArray); 2751 while (inResult && iter) { 2752 inResult = inResult && ArrayGet(&theArray, iter->key, &theValue); 2753 iter = arrayIterateNext(iter); 2754 } 2755 } 2756 else { 2757 POP_STRING(keyStr) 2758 if (ArrayGet(&theArray, keyStr, &theValue)) { 2759 inResult = 1; 2760 } 2761 } 2762 PUSH_INT(inResult) 2763 return(STAT_OK); 2764 } 2765 2766 /* 2767 ** remove a given key from an array unless nDim is 0, then all keys are removed 2768 ** 2769 ** for use with assign-op operators (eg a[i,j] += k 2770 ** Before: Prog-> [nDim], next, ... 2771 ** TheStack-> [indnDim], ... ind1, arrayValue, next, ... 2772 ** After: Prog-> nDim, [next], ... 2773 ** TheStack-> next, ... 2774 */ 2775 static int deleteArrayElement(void) 2776 { 2777 DataValue theArray; 2778 char *keyString = NULL; 2779 int nDim; 2780 2781 nDim = PC->value; 2782 PC++; 2783 2784 DISASM_RT(PC-2, 2); 2785 STACKDUMP(nDim + 1, 3); 2786 2787 if (nDim > 0) { 2788 int errNum; 2789 2790 errNum = makeArrayKeyFromArgs(nDim, &keyString, 0); 2791 if (errNum != STAT_OK) { 2792 return(errNum); 2793 } 2794 } 2795 2796 POP(theArray) 2797 if (theArray.tag == ARRAY_TAG) { 2798 if (nDim > 0) { 2799 ArrayDelete(&theArray, keyString); 2800 } 2801 else { 2802 ArrayDeleteAll(&theArray); 2803 } 2804 } 2805 else { 2806 return(execError("attempt to delete from non-array", NULL)); 2807 } 2808 return(STAT_OK); 2809 } 2810 2811 /* 2812 ** checks errno after operations which can set it. If an error occured, 2813 ** creates appropriate error messages and returns false 2814 */ 2815 static int errCheck(const char *s) 2816 { 2817 if (errno == EDOM) 2818 return execError("%s argument out of domain", s); 2819 else if (errno == ERANGE) 2820 return execError("%s result out of range", s); 2821 else 2822 return STAT_OK; 2823 } 2824 2825 /* 2826 ** combine two strings in a static area and set ErrMsg to point to the 2827 ** result. Returns false so a single return execError() statement can 2828 ** be used to both process the message and return. 2829 */ 2830 static int execError(const char *s1, const char *s2) 2831 { 2832 static char msg[MAX_ERR_MSG_LEN]; 2833 2834 sprintf(msg, s1, s2); 2835 ErrMsg = msg; 2836 return STAT_ERROR; 2837 } 2838 2839 int StringToNum(const char *string, int *number) 2840 { 2841 const char *c = string; 2842 2843 while (*c == ' ' || *c == '\t') { 2844 ++c; 2845 } 2846 if (*c == '+' || *c == '-') { 2847 ++c; 2848 } 2849 while (isdigit((unsigned char)*c)) { 2850 ++c; 2851 } 2852 while (*c == ' ' || *c == '\t') { 2853 ++c; 2854 } 2855 if (*c) { 2856 /* if everything went as expected, we should be at end, but we're not */ 2857 return False; 2858 } 2859 if (number) { 2860 if (sscanf(string, "%d", number) != 1) { 2861 /* This case is here to support old behavior */ 2862 *number = 0; 2863 } 2864 } 2865 return True; 2866 } 2867 2868 #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */ 2869 static void dumpVal(DataValue dv) 2870 { 2871 switch (dv.tag) { 2872 case INT_TAG: 2873 printf("i=%d", dv.val.n); 2874 break; 2875 case STRING_TAG: 2876 { 2877 int k; 2878 char s[21]; 2879 char *src = dv.val.str.rep; 2880 if (!src) { 2881 printf("s=<NULL>"); 2882 } 2883 else { 2884 for (k = 0; src[k] && k < sizeof s - 1; k++) { 2885 s[k] = isprint(src[k]) ? src[k] : '?'; 2886 } 2887 s[k] = 0; 2888 printf("s=\"%s\"%s[%d]", s, 2889 src[k] ? "..." : "", strlen(src)); 2890 } 2891 } 2892 break; 2893 case ARRAY_TAG: 2894 printf("<array>"); 2895 break; 2896 case NO_TAG: 2897 if (!dv.val.inst) { 2898 printf("<no value>"); 2899 } 2900 else { 2901 printf("?%8p", dv.val.inst); 2902 } 2903 break; 2904 default: 2905 printf("UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst); 2906 break; 2907 } 2908 } 2909 #endif /* #ifdef DEBUG_DISASSEMBLER */ 2910 2911 #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */ 2912 static void disasm(Inst *inst, int nInstr) 2913 { 2914 static const char *opNames[N_OPS] = { 2915 "RETURN_NO_VAL", /* returnNoVal */ 2916 "RETURN", /* returnVal */ 2917 "PUSH_SYM", /* pushSymVal */ 2918 "DUP", /* dupStack */ 2919 "ADD", /* add */ 2920 "SUB", /* subtract */ 2921 "MUL", /* multiply */ 2922 "DIV", /* divide */ 2923 "MOD", /* modulo */ 2924 "NEGATE", /* negate */ 2925 "INCR", /* increment */ 2926 "DECR", /* decrement */ 2927 "GT", /* gt */ 2928 "LT", /* lt */ 2929 "GE", /* ge */ 2930 "LE", /* le */ 2931 "EQ", /* eq */ 2932 "NE", /* ne */ 2933 "BIT_AND", /* bitAnd */ 2934 "BIT_OR", /* bitOr */ 2935 "AND", /* and */ 2936 "OR", /* or */ 2937 "NOT", /* not */ 2938 "POWER", /* power */ 2939 "CONCAT", /* concat */ 2940 "ASSIGN", /* assign */ 2941 "SUBR_CALL", /* callSubroutine */ 2942 "FETCH_RET_VAL", /* fetchRetVal */ 2943 "BRANCH", /* branch */ 2944 "BRANCH_TRUE", /* branchTrue */ 2945 "BRANCH_FALSE", /* branchFalse */ 2946 "BRANCH_NEVER", /* branchNever */ 2947 "ARRAY_REF", /* arrayRef */ 2948 "ARRAY_ASSIGN", /* arrayAssign */ 2949 "BEGIN_ARRAY_ITER", /* beginArrayIter */ 2950 "ARRAY_ITER", /* arrayIter */ 2951 "IN_ARRAY", /* inArray */ 2952 "ARRAY_DELETE", /* deleteArrayElement */ 2953 "PUSH_ARRAY_SYM", /* pushArraySymVal */ 2954 "ARRAY_REF_ASSIGN_SETUP", /* arrayRefAndAssignSetup */ 2955 "PUSH_ARG", /* $arg[expr] */ 2956 "PUSH_ARG_COUNT", /* $arg[] */ 2957 "PUSH_ARG_ARRAY" /* $arg */ 2958 }; 2959 int i, j; 2960 2961 printf("\n"); 2962 for (i = 0; i < nInstr; ++i) { 2963 printf("Prog %8p ", &inst[i]); 2964 for (j = 0; j < N_OPS; ++j) { 2965 if (inst[i].func == OpFns[j]) { 2966 printf("%22s ", opNames[j]); 2967 if (j == OP_PUSH_SYM || j == OP_ASSIGN) { 2968 Symbol *sym = inst[i+1].sym; 2969 printf("%s", sym->name); 2970 if (sym->value.tag == STRING_TAG && 2971 strncmp(sym->name, "string #", 8) == 0) { 2972 dumpVal(sym->value); 2973 } 2974 ++i; 2975 } 2976 else if (j == OP_BRANCH || j == OP_BRANCH_FALSE || 2977 j == OP_BRANCH_NEVER || j == OP_BRANCH_TRUE) { 2978 printf("to=(%d) %p", inst[i+1].value, 2979 &inst[i+1] + inst[i+1].value); 2980 ++i; 2981 } 2982 else if (j == OP_SUBR_CALL) { 2983 printf("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value); 2984 i += 2; 2985 } 2986 else if (j == OP_BEGIN_ARRAY_ITER) { 2987 printf("%s in", inst[i+1].sym->name); 2988 ++i; 2989 } 2990 else if (j == OP_ARRAY_ITER) { 2991 printf("%s = %s++ end-loop=(%d) %p", 2992 inst[i+1].sym->name, 2993 inst[i+2].sym->name, 2994 inst[i+3].value, &inst[i+3] + inst[i+3].value); 2995 i += 3; 2996 } 2997 else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE || 2998 j == OP_ARRAY_ASSIGN) { 2999 printf("nDim=%d", inst[i+1].value); 3000 ++i; 3001 } 3002 else if (j == OP_ARRAY_REF_ASSIGN_SETUP) { 3003 printf("binOp=%s ", inst[i+1].value ? "true" : "false"); 3004 printf("nDim=%d", inst[i+2].value); 3005 i += 2; 3006 } 3007 else if (j == OP_PUSH_ARRAY_SYM) { 3008 printf("%s", inst[++i].sym->name); 3009 printf(" %s", inst[i+1].value ? "createAndRef" : "refOnly"); 3010 ++i; 3011 } 3012 3013 printf("\n"); 3014 break; 3015 } 3016 } 3017 if (j == N_OPS) { 3018 printf("%x\n", inst[i].value); 3019 } 3020 } 3021 } 3022 #endif /* #ifdef DEBUG_DISASSEMBLER */ 3023 3024 #ifdef DEBUG_STACK /* for run-time stack dumping */ 3025 #define STACK_DUMP_ARG_PREFIX "Arg" 3026 static void stackdump(int n, int extra) 3027 { 3028 /* TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */ 3029 int nArgs = FP_GET_ARG_COUNT(FrameP); 3030 int i, offset; 3031 char buffer[sizeof(STACK_DUMP_ARG_PREFIX) + TYPE_INT_STR_SIZE(int)]; 3032 printf("Stack ----->\n"); 3033 for (i = 0; i < n + extra; i++) { 3034 char *pos = ""; 3035 DataValue *dv = &StackP[-i - 1]; 3036 if (dv < TheStack) { 3037 printf("--------------Stack base--------------\n"); 3038 break; 3039 } 3040 offset = dv - FrameP; 3041 3042 printf("%4.4s", i < n ? ">>>>" : ""); 3043 printf("%8p ", dv); 3044 switch (offset) { 3045 case 0: pos = "FrameP"; break; /* first local symbol value */ 3046 case FP_ARG_ARRAY_CACHE_INDEX: pos = "args"; break; /* arguments array */ 3047 case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* number of arguments */ 3048 case FP_OLD_FP_INDEX: pos = "OldFP"; break; 3049 case FP_RET_PC_INDEX: pos = "RetPC"; break; 3050 default: 3051 if (offset < -FP_TO_ARGS_DIST && offset >= -FP_TO_ARGS_DIST - nArgs) { 3052 sprintf(pos = buffer, STACK_DUMP_ARG_PREFIX "%d", 3053 offset + FP_TO_ARGS_DIST + nArgs + 1); 3054 } 3055 break; 3056 } 3057 printf("%-6s ", pos); 3058 dumpVal(*dv); 3059 printf("\n"); 3060 } 3061 } 3062 #endif /* ifdef DEBUG_STACK */ 3063