#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "interpret.h"
#include "textBuf.h"
#include "nedit.h"
#include "menu.h"
#include "text.h"
#include "../util/rbTree.h"
#include "../util/nedit_malloc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#ifndef __MVS__
#include <sys/param.h>
#endif
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include "window.h"
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
#define PROGRAM_SIZE 4096
#define MAX_ERR_MSG_LEN 256
#define LOOP_STACK_SIZE 200
#define INSTRUCTION_LIMIT 100
#define NEEDS_BREAK 1
#define NEEDS_CONTINUE 2
#define N_ARGS_ARG_SYM -
1
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 pushArgVal(
void);
static int pushArgCount(
void);
static int pushArgArray(
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);
#if defined(
DEBUG_ASSEMBLY) || defined(
DEBUG_STACK)
#define DEBUG_DISASSEMBLER
static void disasm(Inst *inst,
int nInstr);
#endif
#ifdef DEBUG_ASSEMBLY
#define DISASM(i, n) disasm(i, n)
#else
#define DISASM(i, n)
#endif
#ifdef DEBUG_STACK
static void stackdump(
int n,
int extra);
#define STACKDUMP(n, x) stackdump(n, x)
#define DISASM_RT(i, n) disasm(i, n)
#else
#define STACKDUMP(n, x)
#define DISASM_RT(i, n)
#endif
static Symbol *GlobalSymList =
NULL;
static char *AllocatedStrings =
NULL;
typedef struct SparseArrayEntryWrapperTag {
SparseArrayEntry data;
int inUse;
struct SparseArrayEntryWrapperTag *next;
} SparseArrayEntryWrapper;
static SparseArrayEntryWrapper *AllocatedSparseArrayEntries =
NULL;
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";
static Symbol *LocalSymList =
NULL;
static Inst Prog[
PROGRAM_SIZE];
static Inst *ProgP;
static Inst *LoopStack[
LOOP_STACK_SIZE];
static Inst **LoopStackPtr = LoopStack;
static DataValue *TheStack;
static DataValue *StackP;
static DataValue *FrameP;
static Inst *
PC;
static char *ErrMsg;
static WindowInfo
*InitiatingWindow =
NULL;
static WindowInfo *FocusWindow;
static int PreemptRequest;
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, pushArgVal, pushArgCount, pushArgArray};
#define FP_ARG_ARRAY_CACHE_INDEX (-
1)
#define FP_ARG_COUNT_INDEX (-
2)
#define FP_OLD_FP_INDEX (-
3)
#define FP_RET_PC_INDEX (-
4)
#define FP_TO_ARGS_DIST (
4)
#define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
#define FP_GET_ARG_ARRAY_CACHE(xFrameP) (
FP_GET_ITEM(xFrameP,
FP_ARG_ARRAY_CACHE_INDEX))
#define FP_GET_ARG_COUNT(xFrameP) (
FP_GET_ITEM(xFrameP,
FP_ARG_COUNT_INDEX).val.n)
#define FP_GET_OLD_FP(xFrameP) ((
FP_GET_ITEM(xFrameP,
FP_OLD_FP_INDEX)).val.dataval)
#define FP_GET_RET_PC(xFrameP) ((
FP_GET_ITEM(xFrameP,
FP_RET_PC_INDEX)).val.inst)
#define FP_ARG_START_INDEX(xFrameP) (-(
FP_GET_ARG_COUNT(xFrameP) +
FP_TO_ARGS_DIST))
#define FP_GET_ARG_N(xFrameP,xN) (
FP_GET_ITEM(xFrameP, xN +
FP_ARG_START_INDEX(xFrameP)))
#define FP_GET_SYM_N(xFrameP,xN) (
FP_GET_ITEM(xFrameP, xN))
#define FP_GET_SYM_VAL(xFrameP,xSym) (
FP_GET_SYM_N(xFrameP, xSym->value.val.n))
void InitMacroGlobals(
void)
{
XtActionsRec *actions;
int i, nActions;
static char argName[
3] =
"$x";
static DataValue dv = {
NO_TAG, {
0}};
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);
}
for (i=
0; i<
9; i++) {
argName[
1] =
'1' + i;
dv.val.n = i;
InstallSymbol(argName,
ARG_SYM, dv);
}
dv.val.n =
N_ARGS_ARG_SYM;
InstallSymbol(
"$n_args",
ARG_SYM, dv);
}
void BeginCreatingProgram(
void)
{
LocalSymList =
NULL;
ProgP = Prog;
LoopStackPtr = LoopStack;
}
Program *FinishCreatingProgram(
void)
{
Program *newProg;
int progLen, fpOffset =
0;
Symbol *s;
newProg = (Program *)NEditMalloc(
sizeof(Program));
progLen = ((
char *)ProgP) - ((
char *)Prog);
newProg->code = (Inst *)NEditMalloc(progLen);
memcpy(newProg->code, Prog, progLen);
newProg->localSymList = LocalSymList;
LocalSymList =
NULL;
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);
NEditFree(prog->code);
NEditFree(prog);
}
int AddOp(
int op,
char **msg)
{
if (ProgP >= &Prog[
PROGRAM_SIZE]) {
*msg =
"macro too large";
return 0;
}
ProgP->func = OpFns[op];
ProgP++;
return 1;
}
int AddSym(Symbol *sym,
char **msg)
{
if (ProgP >= &Prog[
PROGRAM_SIZE]) {
*msg =
"macro too large";
return 0;
}
ProgP->sym = sym;
ProgP++;
return 1;
}
int AddImmediate(
int value,
char **msg)
{
if (ProgP >= &Prog[
PROGRAM_SIZE]) {
*msg =
"macro too large";
return 0;
}
ProgP->value = value;
ProgP++;
return 1;
}
int AddBranchOffset(Inst *to,
char **msg)
{
if (ProgP >= &Prog[
PROGRAM_SIZE]) {
*msg =
"macro too large";
return 0;
}
ProgP->value = to - ProgP;
ProgP++;
return 1;
}
Inst *GetPC(
void)
{
return ProgP;
}
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)
reverseCode(start, boundary);
reverseCode(boundary, end);
reverseCode(start, end);
}
void StartLoopAddrList(
void)
{
addLoopAddr(
NULL);
}
int AddBreakAddr(Inst *addr)
{
if (LoopStackPtr == LoopStack)
return 1;
addLoopAddr(addr);
addr->value =
NEEDS_BREAK;
return 0;
}
int AddContinueAddr(Inst *addr)
{
if (LoopStackPtr == LoopStack)
return 1;
addLoopAddr(addr);
addr->value =
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)->value ==
NEEDS_BREAK)
(*LoopStackPtr)->value = breakAddr - *LoopStackPtr;
else if ((*LoopStackPtr)->value ==
NEEDS_CONTINUE)
(*LoopStackPtr)->value = continueAddr - *LoopStackPtr;
else
fprintf(stderr,
"NEdit: internal error (uat) in macro parser\n");
}
}
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;
context = (RestartData *)NEditMalloc(
sizeof(RestartData));
context->stack = (DataValue *)NEditMalloc(
sizeof(DataValue) *
STACK_SIZE);
*continuation = context;
context->stackP = context->stack;
context->pc = prog->code;
context->runWindow = window;
context->focusWindow = window;
for (i=
0; i<nArgs; i++)
*(context->stackP++) = args[i];
context->stackP->val.subr =
NULL;
context->stackP->tag =
NO_TAG;
context->stackP++;
*(context->stackP++) = noValue;
context->stackP->tag =
NO_TAG;
context->stackP->val.n = nArgs;
context->stackP++;
*(context->stackP++) = noValue;
context->frameP = context->stackP;
for (s = prog->localSymList; s !=
NULL; s = s->next) {
FP_GET_SYM_VAL(context->frameP, s) = noValue;
context->stackP++;
}
return ContinueMacro(context, result, msg);
}
int ContinueMacro(RestartData *continuation, DataValue *result,
char **msg)
{
register int status, instCount =
0;
register Inst *inst;
RestartData oldContext;
saveContext(&oldContext);
restoreContext(continuation);
ErrMsg =
NULL;
for (;;) {
inst =
PC++;
status = (inst->func)();
if (status !=
STAT_OK) {
if (status ==
STAT_PREEMPT) {
saveContext(continuation);
restoreContext(&oldContext);
return MACRO_PREEMPT;
}
else if (status ==
STAT_ERROR) {
*msg = ErrMsg;
FreeRestartData(continuation);
restoreContext(&oldContext);
return MACRO_ERROR;
}
else if (status ==
STAT_DONE) {
*msg =
"";
*result = *--StackP;
FreeRestartData(continuation);
restoreContext(&oldContext);
return MACRO_DONE;
}
}
instCount++;
if (instCount >=
INSTRUCTION_LIMIT) {
saveContext(continuation);
restoreContext(&oldContext);
return MACRO_TIME_LIMIT;
}
}
}
void RunMacroAsSubrCall(Program *prog)
{
Symbol *s;
static DataValue noValue = {
NO_TAG, {
0}};
StackP->tag =
NO_TAG;
StackP->val.inst =
PC;
StackP++;
StackP->tag =
NO_TAG;
StackP->val.dataval = FrameP;
StackP++;
StackP->tag =
NO_TAG;
StackP->val.n =
0;
StackP++;
*(StackP++) = noValue;
FrameP = StackP;
PC = prog->code;
for (s = prog->localSymList; s !=
NULL; s = s->next) {
FP_GET_SYM_VAL(FrameP, s) = noValue;
StackP++;
}
}
void FreeRestartData(RestartData *context)
{
NEditFree(context->stack);
NEditFree(context);
}
void PreemptMacro(
void)
{
PreemptRequest = True;
}
void ModifyReturnedValue(RestartData *context, DataValue dv)
{
if ((context->pc-
1)->func == fetchRetVal)
*(context->stackP-
1) = dv;
}
WindowInfo *MacroRunWindow(
void)
{
return InitiatingWindow;
}
WindowInfo *MacroFocusWindow(
void)
{
return FocusWindow;
}
void SetMacroFocusWindow(WindowInfo *window)
{
FocusWindow = window;
}
#define ARRAY_ITER_SYM_PREFIX "aryiter "
Symbol *InstallIteratorSymbol(
void)
{
char symbolName[
sizeof(
ARRAY_ITER_SYM_PREFIX) +
TYPE_INT_STR_SIZE(
int)];
DataValue value;
static int interatorNameIndex =
0;
sprintf(symbolName,
ARRAY_ITER_SYM_PREFIX "#%d", interatorNameIndex);
++interatorNameIndex;
value.tag =
INT_TAG;
value.val.arrayPtr =
NULL;
return(InstallSymbol(symbolName,
LOCAL_SYM, value));
}
Symbol *LookupStringConstSymbol(
const char *value)
{
Symbol *s;
for (s = GlobalSymList; s !=
NULL; s = s->next) {
if (s->type ==
CONST_SYM &&
s->value.tag ==
STRING_TAG &&
!strcmp(s->value.val.str.rep, value)) {
return(s);
}
}
return(
NULL);
}
Symbol *InstallStringConstSymbol(
const char *str)
{
static int stringConstIndex =
0;
char stringName[
35];
DataValue value;
Symbol *sym = LookupStringConstSymbol(str);
if (sym) {
return sym;
}
sprintf(stringName,
"string #%d", stringConstIndex++);
value.tag =
STRING_TAG;
AllocNStringCpy(&value.val.str, str);
return(InstallSymbol(stringName,
CONST_SYM, value));
}
Symbol *LookupSymbol(
const char *name)
{
Symbol *s;
for (s = LocalSymList; s !=
NULL; s = s->next)
if (strcmp(s->name, name) ==
0)
return s;
for (s = GlobalSymList; s !=
NULL; s = s->next)
if (strcmp(s->name, name) ==
0)
return s;
return NULL;
}
Symbol *InstallSymbol(
const char *name,
enum symTypes type, DataValue value)
{
Symbol *s;
s = (Symbol *)NEditMalloc(
sizeof(Symbol));
s->name = NEditStrdup(name);
s->type = type;
s->value = value;
if (type ==
LOCAL_SYM) {
s->next = LocalSymList;
LocalSymList = s;
}
else {
s->next = GlobalSymList;
GlobalSymList = s;
}
return s;
}
Symbol *PromoteToGlobal(Symbol *sym)
{
Symbol *s;
if (sym->type !=
LOCAL_SYM)
return sym;
if (sym == LocalSymList)
LocalSymList = sym->next;
else {
for (s = LocalSymList; s !=
NULL; s = s->next) {
if (s->next == sym) {
s->next = sym->next;
break;
}
}
}
s = LookupSymbol(sym->name);
if (sym == s) {
fprintf(stderr,
"xnedit: To boldly go where no local sym has gone before: %s\n",
sym->name);
sym->type =
GLOBAL_SYM;
return sym;
}
else if (
NULL != s) {
fprintf(stderr,
"xnedit: duplicate symbol in LocalSymList and GlobalSymList: %s\n",
sym->name);
}
sym->type =
GLOBAL_SYM;
sym->next = GlobalSymList;
GlobalSymList = sym;
return sym;
}
#ifdef TRACK_GARBAGE_LEAKS
static int numAllocatedStrings =
0;
static int numAllocatedSparseArrayElements =
0;
#endif
char *AllocString(
int length)
{
char *mem;
mem = (
char*)NEditMalloc(length +
sizeof(
char *) +
1);
*((
char **)mem) = AllocatedStrings;
AllocatedStrings = mem;
#ifdef TRACK_GARBAGE_LEAKS
++numAllocatedStrings;
#endif
return mem +
sizeof(
char *) +
1;
}
int AllocNString(NString *string,
int length)
{
char *mem;
mem = (
char*)NEditMalloc(length +
sizeof(
char *) +
1);
if (!mem) {
string->rep =
0;
string->len =
0;
return False;
}
*((
char **)mem) = AllocatedStrings;
AllocatedStrings = mem;
#ifdef TRACK_GARBAGE_LEAKS
++numAllocatedStrings;
#endif
string->rep = mem +
sizeof(
char *) +
1;
string->rep[length-
1] =
'\0';
string->len = length-
1;
return True;
}
char *AllocStringNCpy(
const char *s,
int length)
{
char *p = AllocString(length +
1);
if (!p)
return p;
if (!s)
s =
"";
p[length] =
'\0';
return strncpy(p, s, length);
}
int AllocNStringNCpy(NString *string,
const char *s,
int length)
{
if (!AllocNString(string, length +
1))
return False;
if (!s)
s =
"";
strncpy(string->rep, s, length);
string->len = strlen(string->rep);
return True;
}
char *AllocStringCpy(
const char *s)
{
return AllocStringNCpy(s, s ? strlen(s) :
0);
}
int AllocNStringCpy(NString *string,
const char *s)
{
size_t length = s ? strlen(s) :
0;
if (!AllocNString(string, length +
1))
return False;
if (s)
strncpy(string->rep, s, length);
return True;
}
static SparseArrayEntry *allocateSparseArrayEntry(
void)
{
SparseArrayEntryWrapper *mem;
mem = (SparseArrayEntryWrapper *)NEditMalloc(
sizeof(SparseArrayEntryWrapper));
mem->next = AllocatedSparseArrayEntries;
AllocatedSparseArrayEntries = mem;
#ifdef TRACK_GARBAGE_LEAKS
++numAllocatedSparseArrayElements;
#endif
return(&(mem->data));
}
static void MarkArrayContentsAsUsed(SparseArrayEntry *arrayPtr)
{
SparseArrayEntry *globalSEUse;
if (arrayPtr) {
((SparseArrayEntryWrapper *)arrayPtr)->inUse =
1;
for (globalSEUse = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)arrayPtr);
globalSEUse !=
NULL;
globalSEUse = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)globalSEUse)) {
((SparseArrayEntryWrapper *)globalSEUse)->inUse =
1;
if (!(*(globalSEUse->key -
1))) {
*(globalSEUse->key -
1) =
1;
}
if (globalSEUse->value.tag ==
STRING_TAG) {
if (!(*(globalSEUse->value.val.str.rep -
1))) {
*(globalSEUse->value.val.str.rep -
1) =
1;
}
}
else if (globalSEUse->value.tag ==
ARRAY_TAG) {
MarkArrayContentsAsUsed(globalSEUse->value.val.arrayPtr);
}
}
}
}
void GarbageCollectStrings(
void)
{
SparseArrayEntryWrapper *nextAP, *thisAP;
char *p, *next;
Symbol *s;
for (p = AllocatedStrings; p !=
NULL; p = *((
char **)p)) {
*(p +
sizeof(
char *)) =
0;
}
for (thisAP = AllocatedSparseArrayEntries;
thisAP !=
NULL; thisAP = thisAP->next) {
thisAP->inUse =
0;
}
for (s = GlobalSymList; s !=
NULL; s = s->next) {
if (s->value.tag ==
STRING_TAG) {
if (!(*(s->value.val.str.rep -
1))) {
*(s->value.val.str.rep -
1) =
1;
}
}
else if (s->value.tag ==
ARRAY_TAG) {
MarkArrayContentsAsUsed(s->value.val.arrayPtr);
}
}
next = AllocatedStrings;
AllocatedStrings =
NULL;
while (next !=
NULL) {
p = next;
next = *((
char **)p);
if (*(p +
sizeof(
char *)) !=
0) {
*((
char **)p) = AllocatedStrings;
AllocatedStrings = p;
}
else {
#ifdef TRACK_GARBAGE_LEAKS
--numAllocatedStrings;
#endif
NEditFree(p);
}
}
nextAP = AllocatedSparseArrayEntries;
AllocatedSparseArrayEntries =
NULL;
while (nextAP !=
NULL) {
thisAP = nextAP;
nextAP = nextAP->next;
if (thisAP->inUse !=
0) {
thisAP->next = AllocatedSparseArrayEntries;
AllocatedSparseArrayEntries = thisAP;
}
else {
#ifdef TRACK_GARBAGE_LEAKS
--numAllocatedSparseArrayElements;
#endif
NEditFree(thisAP);
}
}
#ifdef TRACK_GARBAGE_LEAKS
printf(
"str count = %d\nary count = %d\n", numAllocatedStrings, numAllocatedSparseArrayElements);
#endif
}
static void saveContext(RestartData *context)
{
context->stack = TheStack;
context->stackP = StackP;
context->frameP = FrameP;
context->pc =
PC;
context->runWindow = InitiatingWindow;
context->focusWindow = FocusWindow;
}
static void restoreContext(RestartData *context)
{
TheStack = context->stack;
StackP = context->stackP;
FrameP = context->frameP;
PC = context->pc;
InitiatingWindow = context->runWindow;
FocusWindow = context->focusWindow;
}
static void freeSymbolTable(Symbol *symTab)
{
Symbol *s;
while(symTab !=
NULL) {
s = symTab;
free(s->name);
symTab = s->next;
NEditFree(s);
}
}
#define POP(dataVal) \
if (StackP == TheStack) \
return execError(StackUnderflowMsg,
""); \
dataVal = *--StackP;
#define PUSH(dataVal) \
if (StackP >= &TheStack[
STACK_SIZE]) \
return execError(StackOverflowMsg,
""); \
*StackP++ = dataVal;
#define PEEK(dataVal, peekIndex) \
dataVal = *(StackP - peekIndex -
1);
#define POP_INT(number) \
if (StackP == TheStack) \
return execError(StackUnderflowMsg,
""); \
--StackP; \
if (StackP->tag ==
STRING_TAG) { \
if (!StringToNum(StackP->val.str.rep, &number)) \
return execError(StringToNumberMsg,
""); \
}
else if (StackP->tag ==
INT_TAG) \
number = StackP->val.n; \
else \
return(execError(
"can''t convert array to integer",
NULL));
#define POP_STRING(string) \
if (StackP == TheStack) \
return execError(StackUnderflowMsg,
""); \
--StackP; \
if (StackP->tag ==
INT_TAG) { \
string = AllocString(
TYPE_INT_STR_SIZE(
int)); \
sprintf(string,
"%d", StackP->val.n); \
}
else if (StackP->tag ==
STRING_TAG) \
string = StackP->val.str.rep; \
else \
return(execError(
"can''t convert array to string",
NULL));
#define PEEK_STRING(string, peekIndex) \
if ((StackP - peekIndex -
1)->tag ==
INT_TAG) { \
string = AllocString(
TYPE_INT_STR_SIZE(
int)); \
sprintf(string,
"%d", (StackP - peekIndex -
1)->val.n); \
} \
else if ((StackP - peekIndex -
1)->tag ==
STRING_TAG) { \
string = (StackP - peekIndex -
1)->val.str.rep; \
} \
else { \
return(execError(
"can''t convert array to string",
NULL)); \
}
#define PEEK_INT(number, peekIndex) \
if ((StackP - peekIndex -
1)->tag ==
STRING_TAG) { \
if (!StringToNum((StackP - peekIndex -
1)->val.str.rep, &number)) { \
return execError(StringToNumberMsg,
""); \
} \
}
else if ((StackP - peekIndex -
1)->tag ==
INT_TAG) { \
number = (StackP - peekIndex -
1)->val.n; \
} \
else { \
return(execError(
"can''t convert array to string",
NULL)); \
}
#define PUSH_INT(number) \
if (StackP >= &TheStack[
STACK_SIZE]) \
return execError(StackOverflowMsg,
""); \
StackP->tag =
INT_TAG; \
StackP->val.n = number; \
StackP++;
#define PUSH_STRING(string, length) \
if (StackP >= &TheStack[
STACK_SIZE]) \
return execError(StackOverflowMsg,
""); \
StackP->tag =
STRING_TAG; \
StackP->val.str.rep = string; \
StackP->val.str.len = length; \
StackP++;
#define BINARY_NUMERIC_OPERATION(operator) \
int n1, n2; \
DISASM_RT(
PC-
1,
1); \
STACKDUMP(
2,
3); \
POP_INT(n2) \
POP_INT(n1) \
PUSH_INT(n1 operator n2) \
return STAT_OK;
#define UNARY_NUMERIC_OPERATION(operator) \
int n; \
DISASM_RT(
PC-
1,
1); \
STACKDUMP(
1,
3); \
POP_INT(n) \
PUSH_INT(operator n) \
return STAT_OK;
static int pushSymVal(
void)
{
Symbol *s;
int nArgs, argNum;
DataValue symVal;
DISASM_RT(
PC-
1,
2);
STACKDUMP(
0,
3);
s =
PC->sym;
PC++;
if (s->type ==
LOCAL_SYM) {
symVal =
FP_GET_SYM_VAL(FrameP, s);
}
else if (s->type ==
GLOBAL_SYM || s->type ==
CONST_SYM) {
symVal = s->value;
}
else if (s->type ==
ARG_SYM) {
nArgs =
FP_GET_ARG_COUNT(FrameP);
argNum = s->value.val.n;
if (argNum >= nArgs) {
return execError(
"referenced undefined argument: %s", s->name);
}
if (argNum ==
N_ARGS_ARG_SYM) {
symVal.tag =
INT_TAG;
symVal.val.n = nArgs;
}
else {
symVal =
FP_GET_ARG_N(FrameP, argNum);
}
}
else if (s->type ==
PROC_VALUE_SYM) {
char *errMsg;
if (!(s->value.val.subr)(FocusWindow,
NULL,
0,
&symVal, &errMsg)) {
return execError(errMsg, s->name);
}
}
else
return execError(
"reading non-variable: %s", s->name);
if (symVal.tag ==
NO_TAG) {
return execError(
"variable not set: %s", s->name);
}
PUSH(symVal)
return STAT_OK;
}
static int pushArgVal(
void)
{
int nArgs, argNum;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
1,
3);
POP_INT(argNum)
--argNum;
nArgs =
FP_GET_ARG_COUNT(FrameP);
if (argNum >= nArgs || argNum <
0) {
char argStr[
TYPE_INT_STR_SIZE(argNum)];
sprintf(argStr,
"%d", argNum +
1);
return execError(
"referenced undefined argument: $args[%s]", argStr);
}
PUSH(
FP_GET_ARG_N(FrameP, argNum));
return STAT_OK;
}
static int pushArgCount(
void)
{
DISASM_RT(
PC-
1,
1);
STACKDUMP(
0,
3);
PUSH_INT(
FP_GET_ARG_COUNT(FrameP));
return STAT_OK;
}
static int pushArgArray(
void)
{
int nArgs, argNum;
DataValue argVal, *resultArray;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
0,
3);
nArgs =
FP_GET_ARG_COUNT(FrameP);
resultArray = &
FP_GET_ARG_ARRAY_CACHE(FrameP);
if (resultArray->tag !=
ARRAY_TAG) {
resultArray->tag =
ARRAY_TAG;
resultArray->val.arrayPtr = ArrayNew();
for (argNum =
0; argNum < nArgs; ++argNum) {
char intStr[
TYPE_INT_STR_SIZE(argNum)];
sprintf(intStr,
"%d", argNum +
1);
argVal =
FP_GET_ARG_N(FrameP, argNum);
if (!ArrayInsert(resultArray, AllocStringCpy(intStr), &argVal)) {
return(execError(
"array insertion failure",
NULL));
}
}
}
PUSH(*resultArray);
return STAT_OK;
}
static int pushArraySymVal(
void)
{
Symbol *sym;
DataValue *dataPtr;
int initEmpty;
DISASM_RT(
PC-
1,
3);
STACKDUMP(
0,
3);
sym =
PC->sym;
PC++;
initEmpty =
PC->value;
PC++;
if (sym->type ==
LOCAL_SYM) {
dataPtr = &
FP_GET_SYM_VAL(FrameP, sym);
}
else if (sym->type ==
GLOBAL_SYM) {
dataPtr = &sym->value;
}
else {
return execError(
"assigning to non-lvalue array or non-array: %s", sym->name);
}
if (initEmpty && dataPtr->tag ==
NO_TAG) {
dataPtr->tag =
ARRAY_TAG;
dataPtr->val.arrayPtr = ArrayNew();
}
if (dataPtr->tag ==
NO_TAG) {
return execError(
"variable not set: %s", sym->name);
}
PUSH(*dataPtr)
return STAT_OK;
}
static int assign(
void)
{
Symbol *sym;
DataValue *dataPtr;
DataValue value;
DISASM_RT(
PC-
1,
2);
STACKDUMP(
1,
3);
sym =
PC->sym;
PC++;
if (sym->type !=
GLOBAL_SYM && sym->type !=
LOCAL_SYM) {
if (sym->type ==
ARG_SYM) {
return execError(
"assignment to function argument: %s", sym->name);
}
else if (sym->type ==
PROC_VALUE_SYM) {
return execError(
"assignment to read-only variable: %s", sym->name);
}
else {
return execError(
"assignment to non-variable: %s", sym->name);
}
}
if (sym->type ==
LOCAL_SYM) {
dataPtr = &
FP_GET_SYM_VAL(FrameP, sym);
}
else {
dataPtr = &sym->value;
}
POP(value)
if (value.tag ==
ARRAY_TAG) {
return ArrayCopy(dataPtr, &value);
}
*dataPtr = value;
return STAT_OK;
}
static int dupStack(
void)
{
DataValue value;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
1,
3);
PEEK(value,
0)
PUSH(value)
return STAT_OK;
}
static int add(
void)
{
DataValue leftVal, rightVal, resultArray;
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
PEEK(rightVal,
0)
if (rightVal.tag ==
ARRAY_TAG) {
PEEK(leftVal,
1)
if (leftVal.tag ==
ARRAY_TAG) {
SparseArrayEntry *leftIter, *rightIter;
resultArray.tag =
ARRAY_TAG;
resultArray.val.arrayPtr = ArrayNew();
POP(rightVal)
POP(leftVal)
leftIter = arrayIterateFirst(&leftVal);
rightIter = arrayIterateFirst(&rightVal);
while (leftIter || rightIter) {
Boolean insertResult = True;
if (leftIter && rightIter) {
int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
if (compareResult <
0) {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
else if (compareResult >
0) {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
rightIter = arrayIterateNext(rightIter);
}
else {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
leftIter = arrayIterateNext(leftIter);
rightIter = arrayIterateNext(rightIter);
}
}
else if (leftIter) {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
else {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
rightIter = arrayIterateNext(rightIter);
}
if (!insertResult) {
return(execError(
"array insertion failure",
NULL));
}
}
PUSH(resultArray)
}
else {
return(execError(
"can''t mix math with arrays and non-arrays",
NULL));
}
}
else {
POP_INT(n2)
POP_INT(n1)
PUSH_INT(n1 + n2)
}
return(
STAT_OK);
}
static int subtract(
void)
{
DataValue leftVal, rightVal, resultArray;
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
PEEK(rightVal,
0)
if (rightVal.tag ==
ARRAY_TAG) {
PEEK(leftVal,
1)
if (leftVal.tag ==
ARRAY_TAG) {
SparseArrayEntry *leftIter, *rightIter;
resultArray.tag =
ARRAY_TAG;
resultArray.val.arrayPtr = ArrayNew();
POP(rightVal)
POP(leftVal)
leftIter = arrayIterateFirst(&leftVal);
rightIter = arrayIterateFirst(&rightVal);
while (leftIter) {
Boolean insertResult = True;
if (rightIter) {
int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
if (compareResult <
0) {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
else if (compareResult >
0) {
rightIter = arrayIterateNext(rightIter);
}
else {
leftIter = arrayIterateNext(leftIter);
rightIter = arrayIterateNext(rightIter);
}
}
else {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
if (!insertResult) {
return(execError(
"array insertion failure",
NULL));
}
}
PUSH(resultArray)
}
else {
return(execError(
"can''t mix math with arrays and non-arrays",
NULL));
}
}
else {
POP_INT(n2)
POP_INT(n1)
PUSH_INT(n1 - n2)
}
return(
STAT_OK);
}
static int multiply(
void)
{
BINARY_NUMERIC_OPERATION(*)
}
static int divide(
void)
{
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP_INT(n2)
POP_INT(n1)
if (n2 ==
0) {
return execError(
"division by zero",
"");
}
PUSH_INT(n1 / n2)
return STAT_OK;
}
static int modulo(
void)
{
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP_INT(n2)
POP_INT(n1)
if (n2 ==
0) {
return execError(
"modulo by zero",
"");
}
PUSH_INT(n1 % n2)
return STAT_OK;
}
static int negate(
void)
{
UNARY_NUMERIC_OPERATION(-)
}
static int increment(
void)
{
UNARY_NUMERIC_OPERATION(++)
}
static int decrement(
void)
{
UNARY_NUMERIC_OPERATION(--)
}
static int gt(
void)
{
BINARY_NUMERIC_OPERATION(>)
}
static int lt(
void)
{
BINARY_NUMERIC_OPERATION(<)
}
static int ge(
void)
{
BINARY_NUMERIC_OPERATION(>=)
}
static int le(
void)
{
BINARY_NUMERIC_OPERATION(<=)
}
static int eq(
void)
{
DataValue v1, v2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP(v1)
POP(v2)
if (v1.tag ==
INT_TAG && v2.tag ==
INT_TAG) {
v1.val.n = v1.val.n == v2.val.n;
}
else if (v1.tag ==
STRING_TAG && v2.tag ==
STRING_TAG) {
v1.val.n = !strcmp(v1.val.str.rep, v2.val.str.rep);
}
else if (v1.tag ==
STRING_TAG && v2.tag ==
INT_TAG) {
int number;
if (!StringToNum(v1.val.str.rep, &number)) {
v1.val.n =
0;
}
else {
v1.val.n = number == v2.val.n;
}
}
else if (v2.tag ==
STRING_TAG && v1.tag ==
INT_TAG) {
int number;
if (!StringToNum(v2.val.str.rep, &number)) {
v1.val.n =
0;
}
else {
v1.val.n = number == v1.val.n;
}
}
else {
return(execError(
"incompatible types to compare",
NULL));
}
v1.tag =
INT_TAG;
PUSH(v1)
return(
STAT_OK);
}
static int ne(
void)
{
eq();
return not();
}
static int bitAnd(
void)
{
DataValue leftVal, rightVal, resultArray;
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
PEEK(rightVal,
0)
if (rightVal.tag ==
ARRAY_TAG) {
PEEK(leftVal,
1)
if (leftVal.tag ==
ARRAY_TAG) {
SparseArrayEntry *leftIter, *rightIter;
resultArray.tag =
ARRAY_TAG;
resultArray.val.arrayPtr = ArrayNew();
POP(rightVal)
POP(leftVal)
leftIter = arrayIterateFirst(&leftVal);
rightIter = arrayIterateFirst(&rightVal);
while (leftIter && rightIter) {
Boolean insertResult = True;
int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
if (compareResult <
0) {
leftIter = arrayIterateNext(leftIter);
}
else if (compareResult >
0) {
rightIter = arrayIterateNext(rightIter);
}
else {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
leftIter = arrayIterateNext(leftIter);
rightIter = arrayIterateNext(rightIter);
}
if (!insertResult) {
return(execError(
"array insertion failure",
NULL));
}
}
PUSH(resultArray)
}
else {
return(execError(
"can''t mix math with arrays and non-arrays",
NULL));
}
}
else {
POP_INT(n2)
POP_INT(n1)
PUSH_INT(n1 & n2)
}
return(
STAT_OK);
}
static int bitOr(
void)
{
DataValue leftVal, rightVal, resultArray;
int n1, n2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
PEEK(rightVal,
0)
if (rightVal.tag ==
ARRAY_TAG) {
PEEK(leftVal,
1)
if (leftVal.tag ==
ARRAY_TAG) {
SparseArrayEntry *leftIter, *rightIter;
resultArray.tag =
ARRAY_TAG;
resultArray.val.arrayPtr = ArrayNew();
POP(rightVal)
POP(leftVal)
leftIter = arrayIterateFirst(&leftVal);
rightIter = arrayIterateFirst(&rightVal);
while (leftIter || rightIter) {
Boolean insertResult = True;
if (leftIter && rightIter) {
int compareResult = arrayEntryCompare((rbTreeNode *)leftIter, (rbTreeNode *)rightIter);
if (compareResult <
0) {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
else if (compareResult >
0) {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
rightIter = arrayIterateNext(rightIter);
}
else {
leftIter = arrayIterateNext(leftIter);
rightIter = arrayIterateNext(rightIter);
}
}
else if (leftIter) {
insertResult = ArrayInsert(&resultArray, leftIter->key, &leftIter->value);
leftIter = arrayIterateNext(leftIter);
}
else {
insertResult = ArrayInsert(&resultArray, rightIter->key, &rightIter->value);
rightIter = arrayIterateNext(rightIter);
}
if (!insertResult) {
return(execError(
"array insertion failure",
NULL));
}
}
PUSH(resultArray)
}
else {
return(execError(
"can''t mix math with arrays and non-arrays",
NULL));
}
}
else {
POP_INT(n2)
POP_INT(n1)
PUSH_INT(n1 | n2)
}
return(
STAT_OK);
}
static int and(
void)
{
BINARY_NUMERIC_OPERATION(&&)
}
static int or(
void)
{
BINARY_NUMERIC_OPERATION(||)
}
static int not(
void)
{
UNARY_NUMERIC_OPERATION(!)
}
static int power(
void)
{
int n1, n2, n3;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP_INT(n2)
POP_INT(n1)
if (n2 <
0 && n1 !=
1 && n1 != -
1) {
if (n1 !=
0) {
n3 =
0;
}
else {
n3 = (
int)pow((
double)n1, (
double)n2);
}
}
else {
if ((n1 <
0) && (n2 &
1)) {
n3 = (
int)(pow((
double)n1, (
double)n2) - (
double)
0.
5);
}
else {
n3 = (
int)(pow((
double)n1, (
double)n2) + (
double)
0.
5);
}
}
PUSH_INT(n3)
return errCheck(
"exponentiation");
}
static int concat(
void)
{
char *s1, *s2, *out;
int len1, len2;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP_STRING(s2)
POP_STRING(s1)
len1 = strlen(s1);
len2 = strlen(s2);
out = AllocString(len1 + len2 +
1);
strncpy(out, s1, len1);
strcpy(&out[len1], s2);
PUSH_STRING(out, len1 + len2)
return STAT_OK;
}
static int callSubroutine(
void)
{
Symbol *sym, *s;
int i, nArgs;
static DataValue noValue = {
NO_TAG, {
0}};
Program *prog;
char *errMsg;
sym =
PC->sym;
PC++;
nArgs =
PC->value;
PC++;
DISASM_RT(
PC-
3,
3);
STACKDUMP(nArgs,
3);
if (sym->type ==
C_FUNCTION_SYM) {
DataValue result;
StackP -= nArgs;
PreemptRequest = False;
if (!sym->value.val.subr(FocusWindow, StackP,
nArgs, &result, &errMsg))
return execError(errMsg, sym->name);
if (
PC->func == fetchRetVal) {
if (result.tag ==
NO_TAG) {
return execError(
"%s does not return a value", sym->name);
}
PUSH(result);
PC++;
}
return PreemptRequest ?
STAT_PREEMPT :
STAT_OK;
}
if (sym->type ==
MACRO_FUNCTION_SYM) {
StackP->tag =
NO_TAG;
StackP->val.inst =
PC;
StackP++;
StackP->tag =
NO_TAG;
StackP->val.dataval = FrameP;
StackP++;
StackP->tag =
NO_TAG;
StackP->val.n = nArgs;
StackP++;
*(StackP++) = noValue;
FrameP = StackP;
prog = sym->value.val.prog;
PC = prog->code;
for (s = prog->localSymList; s !=
NULL; s = s->next) {
FP_GET_SYM_VAL(FrameP, s) = noValue;
StackP++;
}
return STAT_OK;
}
if (sym->type ==
ACTION_ROUTINE_SYM) {
String *argList;
Cardinal numArgs = nArgs;
XKeyEvent key_event;
Display *disp;
Window win;
disp=XtDisplay(InitiatingWindow->shell);
win=XtWindow(InitiatingWindow->shell);
key_event.type = KeyPress;
key_event.send_event =
MACRO_EVENT_MARKER;
key_event.time=XtLastTimestampProcessed(XtDisplay(InitiatingWindow->shell));
key_event.display=disp;
key_event.window=key_event.root=key_event.subwindow=win;
argList = (String *)NEditCalloc(nArgs,
sizeof(*argList));
for (i=nArgs-
1; i>=
0; i--) {
POP_STRING(argList[i])
}
PreemptRequest = False;
sym->value.val.xtproc(FocusWindow->lastFocus,
(XEvent *)&key_event, argList, &numArgs);
NEditFree(argList);
if (
PC->func == fetchRetVal) {
return execError(
"%s does not return a value", sym->name);
}
return PreemptRequest ?
STAT_PREEMPT :
STAT_OK;
}
return execError(
"%s is not a function or subroutine", sym->name);
}
static int fetchRetVal(
void)
{
return execError(
"internal error: frv",
NULL);
}
static int returnNoVal(
void)
{
return returnValOrNone(False);
}
static int returnVal(
void)
{
return returnValOrNone(True);
}
static int returnValOrNone(
int valOnStack)
{
DataValue retVal;
static DataValue noValue = {
NO_TAG, {
0}};
DataValue *newFrameP;
int nArgs;
DISASM_RT(
PC-
1,
1);
STACKDUMP(StackP - FrameP +
FP_GET_ARG_COUNT(FrameP) +
FP_TO_ARGS_DIST,
3);
if (valOnStack) {
POP(retVal);
}
nArgs =
FP_GET_ARG_COUNT(FrameP);
newFrameP =
FP_GET_OLD_FP(FrameP);
PC =
FP_GET_RET_PC(FrameP);
StackP = FrameP;
StackP -= (
FP_TO_ARGS_DIST + nArgs);
FrameP = newFrameP;
if (
PC ==
NULL) {
if (valOnStack) {
PUSH(retVal);
}
else {
PUSH(noValue);
}
}
else if (
PC->func == fetchRetVal) {
if (valOnStack) {
PUSH(retVal);
PC++;
}
else {
return execError(
"using return value of %s which does not return a value",
((
PC-
2)->sym->name));
}
}
return PC ==
NULL ?
STAT_DONE :
STAT_OK;
}
static int branch(
void)
{
DISASM_RT(
PC-
1,
2);
STACKDUMP(
0,
3);
PC +=
PC->value;
return STAT_OK;
}
static int branchTrue(
void)
{
int value;
Inst *addr;
DISASM_RT(
PC-
1,
2);
STACKDUMP(
1,
3);
POP_INT(value)
addr =
PC +
PC->value;
PC++;
if (value)
PC = addr;
return STAT_OK;
}
static int branchFalse(
void)
{
int value;
Inst *addr;
DISASM_RT(
PC-
1,
2);
STACKDUMP(
1,
3);
POP_INT(value)
addr =
PC +
PC->value;
PC++;
if (!value)
PC = addr;
return STAT_OK;
}
static int branchNever(
void)
{
DISASM_RT(
PC-
1,
2);
STACKDUMP(
0,
3);
PC++;
return STAT_OK;
}
int ArrayCopy(DataValue *dstArray, DataValue *srcArray)
{
SparseArrayEntry *srcIter;
dstArray->tag =
ARRAY_TAG;
dstArray->val.arrayPtr = ArrayNew();
srcIter = arrayIterateFirst(srcArray);
while (srcIter) {
if (srcIter->value.tag ==
ARRAY_TAG) {
int errNum;
DataValue tmpArray;
errNum = ArrayCopy(&tmpArray, &srcIter->value);
if (errNum !=
STAT_OK) {
return(errNum);
}
if (!ArrayInsert(dstArray, srcIter->key, &tmpArray)) {
return(execError(
"array copy failed",
NULL));
}
}
else {
if (!ArrayInsert(dstArray, srcIter->key, &srcIter->value)) {
return(execError(
"array copy failed",
NULL));
}
}
srcIter = arrayIterateNext(srcIter);
}
return(
STAT_OK);
}
static int makeArrayKeyFromArgs(
int nArgs,
char **keyString,
int leaveParams)
{
DataValue tmpVal;
int sepLen = strlen(
ARRAY_DIM_SEP);
int keyLength =
0;
int i;
keyLength = sepLen * (nArgs -
1);
for (i = nArgs -
1; i >=
0; --i) {
PEEK(tmpVal, i)
if (tmpVal.tag ==
INT_TAG) {
keyLength +=
TYPE_INT_STR_SIZE(tmpVal.val.n);
}
else if (tmpVal.tag ==
STRING_TAG) {
keyLength += tmpVal.val.str.len;
}
else {
return(execError(
"can only index array with string or int.",
NULL));
}
}
*keyString = AllocString(keyLength +
1);
(*keyString)[
0] =
0;
for (i = nArgs -
1; i >=
0; --i) {
if (i != nArgs -
1) {
strcat(*keyString,
ARRAY_DIM_SEP);
}
PEEK(tmpVal, i)
if (tmpVal.tag ==
INT_TAG) {
sprintf(&((*keyString)[strlen(*keyString)]),
"%d", tmpVal.val.n);
}
else if (tmpVal.tag ==
STRING_TAG) {
strcat(*keyString, tmpVal.val.str.rep);
}
else {
return(execError(
"can only index array with string or int.",
NULL));
}
}
if (!leaveParams) {
for (i = nArgs -
1; i >=
0; --i) {
POP(tmpVal)
}
}
return(
STAT_OK);
}
static rbTreeNode *arrayEmptyAllocator(
void)
{
SparseArrayEntry *newNode = allocateSparseArrayEntry();
if (newNode) {
newNode->key =
NULL;
newNode->value.tag =
NO_TAG;
}
return((rbTreeNode *)newNode);
}
static rbTreeNode *arrayAllocateNode(rbTreeNode *src)
{
SparseArrayEntry *newNode = allocateSparseArrayEntry();
if (newNode) {
newNode->key = ((SparseArrayEntry *)src)->key;
newNode->value = ((SparseArrayEntry *)src)->value;
}
return((rbTreeNode *)newNode);
}
static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src)
{
((SparseArrayEntry *)dst)->key = ((SparseArrayEntry *)src)->key;
((SparseArrayEntry *)dst)->value = ((SparseArrayEntry *)src)->value;
return(
1);
}
static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right)
{
return(strcmp(((SparseArrayEntry *)left)->key, ((SparseArrayEntry *)right)->key));
}
static void arrayDisposeNode(rbTreeNode *src)
{
src->left =
NULL;
src->right =
NULL;
src->parent =
NULL;
src->color = -
1;
}
SparseArrayEntry *ArrayNew(
void)
{
return((SparseArrayEntry *)rbTreeNew(arrayEmptyAllocator));
}
Boolean ArrayInsert(DataValue* theArray,
char* keyStr, DataValue* theValue)
{
SparseArrayEntry tmpEntry;
rbTreeNode *insertedNode;
tmpEntry.key = keyStr;
tmpEntry.value = *theValue;
if (theArray->val.arrayPtr ==
NULL) {
theArray->val.arrayPtr = ArrayNew();
}
if (theArray->val.arrayPtr !=
NULL) {
insertedNode = rbTreeInsert((rbTreeNode*) (theArray->val.arrayPtr),
(rbTreeNode *)&tmpEntry, arrayEntryCompare, arrayAllocateNode,
arrayEntryCopyToNode);
if (insertedNode) {
return True;
}
else {
return False;
}
}
return False;
}
void ArrayDelete(DataValue *theArray,
char *keyStr)
{
SparseArrayEntry searchEntry;
if (theArray->val.arrayPtr) {
searchEntry.key = keyStr;
rbTreeDelete((rbTreeNode *)theArray->val.arrayPtr, (rbTreeNode *)&searchEntry,
arrayEntryCompare, arrayDisposeNode);
}
}
void ArrayDeleteAll(DataValue *theArray)
{
if (theArray->val.arrayPtr) {
rbTreeNode *iter = rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
while (iter) {
rbTreeNode *nextIter = rbTreeNext(iter);
rbTreeDeleteNode((rbTreeNode *)theArray->val.arrayPtr,
iter, arrayDisposeNode);
iter = nextIter;
}
}
}
unsigned ArraySize(DataValue* theArray)
{
if (theArray->val.arrayPtr) {
return rbTreeSize((rbTreeNode *)theArray->val.arrayPtr);
}
else {
return 0;
}
}
Boolean ArrayGet(DataValue* theArray,
char* keyStr, DataValue* theValue)
{
SparseArrayEntry searchEntry;
rbTreeNode *foundNode;
if (theArray->val.arrayPtr) {
searchEntry.key = keyStr;
foundNode = rbTreeFind((rbTreeNode*) theArray->val.arrayPtr,
(rbTreeNode*) &searchEntry, arrayEntryCompare);
if (foundNode) {
*theValue = ((SparseArrayEntry*) foundNode)->value;
return True;
}
}
return False;
}
SparseArrayEntry *arrayIterateFirst(DataValue *theArray)
{
SparseArrayEntry *startPos;
if (theArray->val.arrayPtr) {
startPos = (SparseArrayEntry *)rbTreeBegin((rbTreeNode *)theArray->val.arrayPtr);
}
else {
startPos =
NULL;
}
return(startPos);
}
SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator)
{
SparseArrayEntry *nextPos;
if (iterator) {
nextPos = (SparseArrayEntry *)rbTreeNext((rbTreeNode *)iterator);
}
else {
nextPos =
NULL;
}
return(nextPos);
}
static int arrayRef(
void)
{
int errNum;
DataValue srcArray, valueItem;
char *keyString =
NULL;
int nDim;
nDim =
PC->value;
PC++;
DISASM_RT(
PC-
2,
2);
STACKDUMP(nDim,
3);
if (nDim >
0) {
errNum = makeArrayKeyFromArgs(nDim, &keyString,
0);
if (errNum !=
STAT_OK) {
return(errNum);
}
POP(srcArray)
if (srcArray.tag ==
ARRAY_TAG) {
if (!ArrayGet(&srcArray, keyString, &valueItem)) {
return(execError(
"referenced array value not in array: %s", keyString));
}
PUSH(valueItem)
return(
STAT_OK);
}
else {
return(execError(
"operator [] on non-array",
NULL));
}
}
else {
POP(srcArray)
if (srcArray.tag ==
ARRAY_TAG) {
PUSH_INT(ArraySize(&srcArray))
return(
STAT_OK);
}
else {
return(execError(
"operator [] on non-array",
NULL));
}
}
}
static int arrayAssign(
void)
{
char *keyString =
NULL;
DataValue srcValue, dstArray;
int errNum;
int nDim;
nDim =
PC->value;
PC++;
DISASM_RT(
PC-
2,
1);
STACKDUMP(nDim,
3);
if (nDim >
0) {
POP(srcValue)
errNum = makeArrayKeyFromArgs(nDim, &keyString,
0);
if (errNum !=
STAT_OK) {
return(errNum);
}
POP(dstArray)
if (dstArray.tag !=
ARRAY_TAG && dstArray.tag !=
NO_TAG) {
return(execError(
"cannot assign array element of non-array",
NULL));
}
if (srcValue.tag ==
ARRAY_TAG) {
DataValue arrayCopyValue;
errNum = ArrayCopy(&arrayCopyValue, &srcValue);
srcValue = arrayCopyValue;
if (errNum !=
STAT_OK) {
return(errNum);
}
}
if (ArrayInsert(&dstArray, keyString, &srcValue)) {
return(
STAT_OK);
}
else {
return(execError(
"array member allocation failure",
NULL));
}
}
return(execError(
"empty operator []",
NULL));
}
static int arrayRefAndAssignSetup(
void)
{
int errNum;
DataValue srcArray, valueItem, moveExpr;
char *keyString =
NULL;
int binaryOp, nDim;
binaryOp =
PC->value;
PC++;
nDim =
PC->value;
PC++;
DISASM_RT(
PC-
3,
3);
STACKDUMP(nDim +
1,
3);
if (binaryOp) {
POP(moveExpr)
}
if (nDim >
0) {
errNum = makeArrayKeyFromArgs(nDim, &keyString,
1);
if (errNum !=
STAT_OK) {
return(errNum);
}
PEEK(srcArray, nDim)
if (srcArray.tag ==
ARRAY_TAG) {
if (!ArrayGet(&srcArray, keyString, &valueItem)) {
return(execError(
"referenced array value not in array: %s", keyString));
}
PUSH(valueItem)
if (binaryOp) {
PUSH(moveExpr)
}
return(
STAT_OK);
}
else {
return(execError(
"operator [] on non-array",
NULL));
}
}
else {
return(execError(
"array[] not an lvalue",
NULL));
}
}
static int beginArrayIter(
void)
{
Symbol *iterator;
DataValue *iteratorValPtr;
DataValue arrayVal;
DISASM_RT(
PC-
1,
2);
STACKDUMP(
1,
3);
iterator =
PC->sym;
PC++;
POP(arrayVal)
if (iterator->type ==
LOCAL_SYM) {
iteratorValPtr = &
FP_GET_SYM_VAL(FrameP, iterator);
}
else {
return(execError(
"bad temporary iterator: %s", iterator->name));
}
iteratorValPtr->tag =
INT_TAG;
if (arrayVal.tag !=
ARRAY_TAG) {
return(execError(
"can''t iterate non-array",
NULL));
}
iteratorValPtr->val.arrayPtr = arrayIterateFirst(&arrayVal);
return(
STAT_OK);
}
static int arrayIter(
void)
{
Symbol *iterator;
Symbol *item;
DataValue *iteratorValPtr;
DataValue *itemValPtr;
SparseArrayEntry *thisEntry;
Inst *branchAddr;
DISASM_RT(
PC-
1,
4);
STACKDUMP(
0,
3);
item =
PC->sym;
PC++;
iterator =
PC->sym;
PC++;
branchAddr =
PC +
PC->value;
PC++;
if (item->type ==
LOCAL_SYM) {
itemValPtr = &
FP_GET_SYM_VAL(FrameP, item);
}
else if (item->type ==
GLOBAL_SYM) {
itemValPtr = &(item->value);
}
else {
return(execError(
"can''t assign to: %s", item->name));
}
itemValPtr->tag =
NO_TAG;
if (iterator->type ==
LOCAL_SYM) {
iteratorValPtr = &
FP_GET_SYM_VAL(FrameP, iterator);
}
else {
return(execError(
"bad temporary iterator: %s", iterator->name));
}
thisEntry = iteratorValPtr->val.arrayPtr;
if (thisEntry && thisEntry->nodePtrs.color != -
1) {
itemValPtr->tag =
STRING_TAG;
itemValPtr->val.str.rep = thisEntry->key;
itemValPtr->val.str.len = strlen(thisEntry->key);
iteratorValPtr->val.arrayPtr = arrayIterateNext(thisEntry);
}
else {
PC = branchAddr;
}
return(
STAT_OK);
}
static int inArray(
void)
{
DataValue theArray, leftArray, theValue;
char *keyStr;
int inResult =
0;
DISASM_RT(
PC-
1,
1);
STACKDUMP(
2,
3);
POP(theArray)
if (theArray.tag !=
ARRAY_TAG) {
return(execError(
"operator in on non-array",
NULL));
}
PEEK(leftArray,
0)
if (leftArray.tag ==
ARRAY_TAG) {
SparseArrayEntry *iter;
POP(leftArray)
inResult =
1;
iter = arrayIterateFirst(&leftArray);
while (inResult && iter) {
inResult = inResult && ArrayGet(&theArray, iter->key, &theValue);
iter = arrayIterateNext(iter);
}
}
else {
POP_STRING(keyStr)
if (ArrayGet(&theArray, keyStr, &theValue)) {
inResult =
1;
}
}
PUSH_INT(inResult)
return(
STAT_OK);
}
static int deleteArrayElement(
void)
{
DataValue theArray;
char *keyString =
NULL;
int nDim;
nDim =
PC->value;
PC++;
DISASM_RT(
PC-
2,
2);
STACKDUMP(nDim +
1,
3);
if (nDim >
0) {
int errNum;
errNum = makeArrayKeyFromArgs(nDim, &keyString,
0);
if (errNum !=
STAT_OK) {
return(errNum);
}
}
POP(theArray)
if (theArray.tag ==
ARRAY_TAG) {
if (nDim >
0) {
ArrayDelete(&theArray, keyString);
}
else {
ArrayDeleteAll(&theArray);
}
}
else {
return(execError(
"attempt to delete from non-array",
NULL));
}
return(
STAT_OK);
}
static int errCheck(
const char *s)
{
if (errno ==
EDOM)
return execError(
"%s argument out of domain", s);
else if (errno ==
ERANGE)
return execError(
"%s result out of range", s);
else
return STAT_OK;
}
static int execError(
const char *s1,
const char *s2)
{
static char msg[
MAX_ERR_MSG_LEN];
sprintf(msg, s1, s2);
ErrMsg = msg;
return STAT_ERROR;
}
int StringToNum(
const char *string,
int *number)
{
const char *c = string;
while (*c ==
' ' || *c ==
'\t') {
++c;
}
if (*c ==
'+' || *c ==
'-') {
++c;
}
while (isdigit((
unsigned char)*c)) {
++c;
}
while (*c ==
' ' || *c ==
'\t') {
++c;
}
if (*c) {
return False;
}
if (number) {
if (sscanf(string,
"%d", number) !=
1) {
*number =
0;
}
}
return True;
}
#ifdef DEBUG_DISASSEMBLER
static void dumpVal(DataValue dv)
{
switch (dv.tag) {
case INT_TAG:
printf(
"i=%d", dv.val.n);
break;
case STRING_TAG:
{
int k;
char s[
21];
char *src = dv.val.str.rep;
if (!src) {
printf(
"s=<NULL>");
}
else {
for (k =
0; src[k] && k <
sizeof s -
1; k++) {
s[k] = isprint(src[k]) ? src[k] :
'?';
}
s[k] =
0;
printf(
"s=\"%s\"%s[%d]", s,
src[k] ?
"..." :
"", strlen(src));
}
}
break;
case ARRAY_TAG:
printf(
"<array>");
break;
case NO_TAG:
if (!dv.val.inst) {
printf(
"<no value>");
}
else {
printf(
"?%8p", dv.val.inst);
}
break;
default:
printf(
"UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst);
break;
}
}
#endif
#ifdef DEBUG_DISASSEMBLER
static void disasm(Inst *inst,
int nInstr)
{
static const char *opNames[
N_OPS] = {
"RETURN_NO_VAL",
"RETURN",
"PUSH_SYM",
"DUP",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"NEGATE",
"INCR",
"DECR",
"GT",
"LT",
"GE",
"LE",
"EQ",
"NE",
"BIT_AND",
"BIT_OR",
"AND",
"OR",
"NOT",
"POWER",
"CONCAT",
"ASSIGN",
"SUBR_CALL",
"FETCH_RET_VAL",
"BRANCH",
"BRANCH_TRUE",
"BRANCH_FALSE",
"BRANCH_NEVER",
"ARRAY_REF",
"ARRAY_ASSIGN",
"BEGIN_ARRAY_ITER",
"ARRAY_ITER",
"IN_ARRAY",
"ARRAY_DELETE",
"PUSH_ARRAY_SYM",
"ARRAY_REF_ASSIGN_SETUP",
"PUSH_ARG",
"PUSH_ARG_COUNT",
"PUSH_ARG_ARRAY"
};
int i, j;
printf(
"\n");
for (i =
0; i < nInstr; ++i) {
printf(
"Prog %8p ", &inst[i]);
for (j =
0; j <
N_OPS; ++j) {
if (inst[i].func == OpFns[j]) {
printf(
"%22s ", opNames[j]);
if (j ==
OP_PUSH_SYM || j ==
OP_ASSIGN) {
Symbol *sym = inst[i+
1].sym;
printf(
"%s", sym->name);
if (sym->value.tag ==
STRING_TAG &&
strncmp(sym->name,
"string #",
8) ==
0) {
dumpVal(sym->value);
}
++i;
}
else if (j ==
OP_BRANCH || j ==
OP_BRANCH_FALSE ||
j ==
OP_BRANCH_NEVER || j ==
OP_BRANCH_TRUE) {
printf(
"to=(%d) %p", inst[i+
1].value,
&inst[i+
1] + inst[i+
1].value);
++i;
}
else if (j ==
OP_SUBR_CALL) {
printf(
"%s (%d arg)", inst[i+
1].sym->name, inst[i+
2].value);
i +=
2;
}
else if (j ==
OP_BEGIN_ARRAY_ITER) {
printf(
"%s in", inst[i+
1].sym->name);
++i;
}
else if (j ==
OP_ARRAY_ITER) {
printf(
"%s = %s++ end-loop=(%d) %p",
inst[i+
1].sym->name,
inst[i+
2].sym->name,
inst[i+
3].value, &inst[i+
3] + inst[i+
3].value);
i +=
3;
}
else if (j ==
OP_ARRAY_REF || j ==
OP_ARRAY_DELETE ||
j ==
OP_ARRAY_ASSIGN) {
printf(
"nDim=%d", inst[i+
1].value);
++i;
}
else if (j ==
OP_ARRAY_REF_ASSIGN_SETUP) {
printf(
"binOp=%s ", inst[i+
1].value ?
"true" :
"false");
printf(
"nDim=%d", inst[i+
2].value);
i +=
2;
}
else if (j ==
OP_PUSH_ARRAY_SYM) {
printf(
"%s", inst[++i].sym->name);
printf(
" %s", inst[i+
1].value ?
"createAndRef" :
"refOnly");
++i;
}
printf(
"\n");
break;
}
}
if (j ==
N_OPS) {
printf(
"%x\n", inst[i].value);
}
}
}
#endif
#ifdef DEBUG_STACK
#define STACK_DUMP_ARG_PREFIX "Arg"
static void stackdump(
int n,
int extra)
{
int nArgs =
FP_GET_ARG_COUNT(FrameP);
int i, offset;
char buffer[
sizeof(
STACK_DUMP_ARG_PREFIX) +
TYPE_INT_STR_SIZE(
int)];
printf(
"Stack ----->\n");
for (i =
0; i < n + extra; i++) {
char *pos =
"";
DataValue *dv = &StackP[-i -
1];
if (dv < TheStack) {
printf(
"--------------Stack base--------------\n");
break;
}
offset = dv - FrameP;
printf(
"%4.4s", i < n ?
">>>>" :
"");
printf(
"%8p ", dv);
switch (offset) {
case 0: pos =
"FrameP";
break;
case FP_ARG_ARRAY_CACHE_INDEX: pos =
"args";
break;
case FP_ARG_COUNT_INDEX: pos =
"NArgs";
break;
case FP_OLD_FP_INDEX: pos =
"OldFP";
break;
case FP_RET_PC_INDEX: pos =
"RetPC";
break;
default:
if (offset < -
FP_TO_ARGS_DIST && offset >= -
FP_TO_ARGS_DIST - nArgs) {
sprintf(pos = buffer,
STACK_DUMP_ARG_PREFIX "%d",
offset +
FP_TO_ARGS_DIST + nArgs +
1);
}
break;
}
printf(
"%-6s ", pos);
dumpVal(*dv);
printf(
"\n");
}
}
#endif