📄 interpret.c
字号:
static const char CVSID[] = "$Id: interpret.c,v 1.33.2.1 2003/10/20 16:38:57 tringali Exp $";/******************************************************************************** ** interpret.c -- Nirvana Editor macro interpreter ** ** Copyright (C) 1999 Mark Edel ** ** This is free software; you can redistribute it and/or modify it under the ** terms of the GNU General Public License as published by the Free Software ** Foundation; either version 2 of the License, or (at your option) any later ** version. ** ** This software is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ** for more details. ** ** You should have received a copy of the GNU General Public License along with ** software; if not, write to the Free Software Foundation, Inc., 59 Temple ** Place, Suite 330, Boston, MA 02111-1307 USA ** ** Nirvana Text Editor ** April, 1997 ** ** Written by Mark Edel ** ********************************************************************************/#ifdef HAVE_CONFIG_H#include "../config.h"#endif#include "interpret.h"#include "textBuf.h"#include "nedit.h"#include "menu.h"#include "text.h"#include "rbTree.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include <limits.h>#include <ctype.h>#include <errno.h>#ifdef VMS#include "../util/VMSparam.h"#else#ifndef __MVS__#include <sys/param.h>#endif#endif /*VMS*/#include <X11/Intrinsic.h>#include <Xm/Xm.h>#ifdef HAVE_DEBUG_H#include "../debug.h"#endif#define PROGRAM_SIZE 4096 /* Maximum program size */#define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */#define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts allowed per program */#define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is allowed to execute before preempting and returning to allow other things to run *//* Temporary markers placed in a branch address location to designate which loop address (break or continue) the location needs */#define NEEDS_BREAK (Inst)1#define NEEDS_CONTINUE (Inst)2#define N_ARGS_ARG_SYM -1 /* special arg number meaning $n_args value */enum opStatusCodes {STAT_OK=2, STAT_DONE, STAT_ERROR, STAT_PREEMPT};static void addLoopAddr(Inst *addr);static void saveContext(RestartData *context);static void restoreContext(RestartData *context);static int returnNoVal(void);static int returnVal(void);static int returnValOrNone(int valOnStack);static int pushSymVal(void);static int pushArraySymVal(void);static int dupStack(void);static int add(void);static int subtract(void);static int multiply(void);static int divide(void);static int modulo(void);static int negate(void);static int increment(void);static int decrement(void);static int gt(void);static int lt(void);static int ge(void);static int le(void);static int eq(void);static int ne(void);static int bitAnd(void);static int bitOr(void);static int and(void);static int or(void);static int not(void);static int power(void);static int concat(void);static int assign(void);static int callSubroutine(void);static int fetchRetVal(void);static int branch(void);static int branchTrue(void);static int branchFalse(void);static int branchNever(void);static int arrayRef(void);static int arrayAssign(void);static int arrayRefAndAssignSetup(void);static int beginArrayIter(void);static int arrayIter(void);static int inArray(void);static int deleteArrayElement(void);static void freeSymbolTable(Symbol *symTab);static int errCheck(const char *s);static int execError(const char *s1, const char *s2);static rbTreeNode *arrayEmptyAllocator(void);static rbTreeNode *arrayAllocateNode(rbTreeNode *src);static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src);static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right);static void arrayDisposeNode(rbTreeNode *src);static SparseArrayEntry *allocateSparseArrayEntry(void);/*#define DEBUG_ASSEMBLY*//*#define DEBUG_STACK*/#if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK)#define DEBUG_DISASSEMBLERstatic void disasm(Inst *inst, int nInstr);#endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */#ifdef DEBUG_ASSEMBLY /* for disassembly */#define DISASM(i, n) disasm(i, n)#else /* #ifndef DEBUG_ASSEMBLY */#define DISASM(i, n)#endif /* #ifndef DEBUG_ASSEMBLY */#ifdef DEBUG_STACK /* for run-time instruction and stack trace */static void stackdump(int n, int extra);#define STACKDUMP(n, x) stackdump(n, x)#define DISASM_RT(i, n) disasm(i, n)#else /* #ifndef DEBUG_STACK */#define STACKDUMP(n, x)#define DISASM_RT(i, n)#endif /* #ifndef DEBUG_STACK *//* Global symbols and function definitions */static Symbol *GlobalSymList = NULL;/* List of all memory allocated for strings */static char *AllocatedStrings = NULL;typedef struct { SparseArrayEntry data; /* LEAVE this as top entry */ int inUse; /* we use pointers to the data to refer to the entire struct */ struct SparseArrayEntryWrapper *next;} SparseArrayEntryWrapper;static SparseArrayEntryWrapper *AllocatedSparseArrayEntries = NULL; /* Message strings used in macros (so they don't get repeated every time the macros are used */static const char *StackOverflowMsg = "macro stack overflow";static const char *StackUnderflowMsg = "macro stack underflow";static const char *StringToNumberMsg = "string could not be converted to number";/* Temporary global data for use while accumulating programs */static Symbol *LocalSymList = NULL; /* symbols local to the program */static Inst Prog[PROGRAM_SIZE]; /* the program */static Inst *ProgP; /* next free spot for code gen. */static Inst *LoopStack[LOOP_STACK_SIZE]; /* addresses of break, cont stmts */static Inst **LoopStackPtr = LoopStack; /* to fill at the end of a loop *//* Global data for the interpreter */static DataValue *Stack; /* the stack */static DataValue *StackP; /* next free spot on stack */static DataValue *FrameP; /* frame pointer (start of local variables for the current subroutine invocation) */static Inst *PC; /* program counter during execution */static char *ErrMsg; /* global for returning error messages from executing functions */static WindowInfo *InitiatingWindow = NULL; /* window from which macro was run */static WindowInfo *FocusWindow; /* window on which macro commands operate */static int PreemptRequest; /* passes preemption requests from called routines back up to the interpreter *//* Array for mapping operations to functions for performing the operations Must correspond to the enum called "operations" in interpret.h */static int (*OpFns[N_OPS])() = {returnNoVal, returnVal, pushSymVal, dupStack, add, subtract, multiply, divide, modulo, negate, increment, decrement, gt, lt, ge, le, eq, ne, bitAnd, bitOr, and, or, not, power, concat, assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse, branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray, deleteArrayElement, pushArraySymVal, arrayRefAndAssignSetup};/*** Initialize macro language global variables. Must be called before** any macros are even parsed, because the parser uses action routine** symbols to comprehend hyphenated names.*/void InitMacroGlobals(void){ XtActionsRec *actions; int i, nActions; static char argName[3] = "$x"; static DataValue dv = {NO_TAG, {0}}; /* Add action routines from NEdit menus and text widget */ actions = GetMenuActions(&nActions); for (i=0; i<nActions; i++) { dv.val.xtproc = actions[i].proc; InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv); } actions = TextGetActions(&nActions); for (i=0; i<nActions; i++) { dv.val.xtproc = actions[i].proc; InstallSymbol(actions[i].string, ACTION_ROUTINE_SYM, dv); } /* Add subroutine argument symbols ($1, $2, ..., $9) */ for (i=0; i<9; i++) { argName[1] = '1' + i; dv.val.n = i; InstallSymbol(argName, ARG_SYM, dv); } /* Add special symbol $n_args */ dv.val.n = N_ARGS_ARG_SYM; InstallSymbol("$n_args", ARG_SYM, dv);}/*** To build a program for the interpreter, call BeginCreatingProgram, to** begin accumulating the program, followed by calls to AddOp, AddSym,** and InstallSymbol to add symbols and operations. When the new program** is finished, collect the results with FinishCreatingProgram. This returns** a self contained program that can be run with ExecuteMacro.*//*** Start collecting instructions for a program. Clears the program** and the symbol table.*/void BeginCreatingProgram(void){ LocalSymList = NULL; ProgP = Prog; LoopStackPtr = LoopStack;}/*** Finish up the program under construction, and return it (code and** symbol table) as a package that ExecuteMacro can execute. This** program must be freed with FreeProgram.*/Program *FinishCreatingProgram(void){ Program *newProg; int progLen, fpOffset = 0; Symbol *s; newProg = (Program *)XtMalloc(sizeof(Program)); progLen = ((char *)ProgP) - ((char *)Prog); newProg->code = (Inst *)XtMalloc(progLen); memcpy(newProg->code, Prog, progLen); newProg->localSymList = LocalSymList; LocalSymList = NULL; /* Local variables' values are stored on the stack. Here we assign frame pointer offsets to them. */ for (s = newProg->localSymList; s != NULL; s = s->next) s->value.val.n = fpOffset++; DISASM(newProg->code, ProgP - Prog); return newProg;}void FreeProgram(Program *prog){ freeSymbolTable(prog->localSymList); XtFree((char *)prog->code); XtFree((char *)prog); }/*** Add an operator (instruction) to the end of the current program*/int AddOp(int op, char **msg){ if (ProgP >= &Prog[PROGRAM_SIZE]) { *msg = "macro too large"; return 0; } *ProgP++ = OpFns[op]; return 1;}/*** Add a symbol operand to the current program*/int AddSym(Symbol *sym, char **msg){ if (ProgP >= &Prog[PROGRAM_SIZE]) { *msg = "macro too large"; return 0; } *ProgP++ = (Inst)sym; return 1;}/*** Add an immediate value operand to the current program*/int AddImmediate(void *value, char **msg){ if (ProgP >= &Prog[PROGRAM_SIZE]) { *msg = "macro too large"; return 0; } *ProgP++ = (Inst)value; return 1;}/*** Add a branch offset operand to the current program*/int AddBranchOffset(Inst *to, char **msg){ if (ProgP >= &Prog[PROGRAM_SIZE]) { *msg = "macro too large"; return 0; } *ProgP = (Inst)(to - ProgP); ProgP++; return 1;}/*** Return the address at which the next instruction will be stored*/Inst *GetPC(void){ return ProgP;}/*** Swap the positions of two contiguous blocks of code. The first block** running between locations start and boundary, and the second between** boundary and end.*/void SwapCode(Inst *start, Inst *boundary, Inst *end){#define reverseCode(L, H) \ do { register Inst t, *l = L, *h = H - 1; \ while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0) /* double-reverse method: reverse elements of both parts then whole lot */ /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */ reverseCode(start, boundary); /* 1 */ reverseCode(boundary, end); /* 2 */ reverseCode(start, end); /* 3 */}/*** Maintain a stack to save addresses of branch operations for break and** continue statements, so they can be filled in once the information** on where to branch is known.**** Call StartLoopAddrList at the beginning of a loop, AddBreakAddr or** AddContinueAddr to register the address at which to store the branch** address for a break or continue statement, and FillLoopAddrs to fill** in all the addresses and return to the level of the enclosing loop.*/void StartLoopAddrList(void){ addLoopAddr(NULL);}int AddBreakAddr(Inst *addr){ if (LoopStackPtr == LoopStack) return 1; addLoopAddr(addr); *addr = NEEDS_BREAK; return 0;}int AddContinueAddr(Inst *addr){ if (LoopStackPtr == LoopStack) return 1; addLoopAddr(addr); *addr = NEEDS_CONTINUE; return 0;}static void addLoopAddr(Inst *addr){ if (LoopStackPtr > &LoopStack[LOOP_STACK_SIZE-1]) { fprintf(stderr, "NEdit: loop stack overflow in macro parser"); return; } *LoopStackPtr++ = addr;}void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr){ while (True) { LoopStackPtr--; if (LoopStackPtr < LoopStack) { fprintf(stderr, "NEdit: internal error (lsu) in macro parser\n"); return; } if (*LoopStackPtr == NULL) break; if (**LoopStackPtr == NEEDS_BREAK) **(Inst ***)LoopStackPtr = (Inst *)(breakAddr - *LoopStackPtr); else if (**LoopStackPtr == NEEDS_CONTINUE) **(Inst ***)LoopStackPtr = (Inst *)(continueAddr - *LoopStackPtr); else fprintf(stderr, "NEdit: internal error (uat) in macro parser\n"); }}/*** Execute a compiled macro, "prog", using the arguments in the array** "args". Returns one of MACRO_DONE, MACRO_PREEMPT, or MACRO_ERROR.** if MACRO_DONE is returned, the macro completed, and the returned value** (if any) can be read from "result". If MACRO_PREEMPT is returned, the** macro exceeded its alotted time-slice and scheduled...*/int ExecuteMacro(WindowInfo *window, Program *prog, int nArgs, DataValue *args, DataValue *result, RestartData **continuation, char **msg){ RestartData *context; static DataValue noValue = {NO_TAG, {0}}; Symbol *s; int i; /* Create an execution context (a stack, a stack pointer, a frame pointer, and a program counter) which will retain the program state across preemption and resumption of execution */ context = (RestartData *)XtMalloc(sizeof(RestartData)); context->stack = (DataValue *)XtMalloc(sizeof(DataValue) * STACK_SIZE); *continuation = context; context->stackP = context->stack; context->pc = prog->code; context->runWindow = window; context->focusWindow = window; /* Push arguments and call information onto the stack */ for (i=0; i<nArgs; i++) *(context->stackP++) = args[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -