This removes the ugly parsing of the "define" keyword. It solves this by
saving and restoring the state of the program accumulation. This solves
a long standing problam, that parts between "define" definition aren't
in the same scope, i.e. this works now:
a = 0
define t {
}
t_print(a "\n")
---
source/interpret.c | 23 ++++++--
source/interpret.h | 16 +++++
source/macro.c | 139 +++++++++------------------------------------------
source/nedit.c | 2
source/parse.h | 2
source/parse.y | 56 +++++++++++++++-----
source/smartIndent.c | 8 +-
source/userCmds.c | 4 -
8 files changed, 109 insertions(+), 141 deletions(-)
diff --quilt old/source/interpret.c new/source/interpret.c
--- old/source/interpret.c
+++ new/source/interpret.c
@@ -60,14 +60,11 @@ static const char CVSID[] = "$Id: interp
#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
@@ -272,23 +269,30 @@ void InitMacroGlobals(void)
/*
** Start collecting instructions for a program. Clears the program
** and the symbol table.
*/
-void BeginCreatingProgram(void)
-{
+void BeginCreatingProgram(AccumulatorData *acc)
+{
+ /* save state */
+ acc->localSymList = LocalSymList;
+ memcpy(acc->prog, Prog, sizeof(Inst) * PROGRAM_SIZE);
+ acc->progP = ProgP;
+ memcpy(acc->loopStack, LoopStack, sizeof(Inst) * LOOP_STACK_SIZE);
+ acc->loopStackPtr = LoopStackPtr;
+
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 *FinishCreatingProgram(AccumulatorData *acc)
{
Program *newProg;
int progLen, fpOffset = 0;
Symbol *s;
@@ -304,10 +308,17 @@ Program *FinishCreatingProgram(void)
for (s = newProg->localSymList; s != NULL; s = s->next)
s->value.val.n = fpOffset++;
DISASM(newProg->code, ProgP - Prog);
+ /* restore state */
+ LocalSymList = acc->localSymList;
+ memcpy(Prog, acc->prog, sizeof(Inst) * PROGRAM_SIZE);
+ ProgP = acc->progP;
+ memcpy(LoopStack, acc->loopStack, sizeof(Inst) * LOOP_STACK_SIZE);
+ LoopStackPtr = acc->loopStackPtr;
+
return newProg;
}
void FreeProgram(Program *prog)
{
diff --quilt old/source/interpret.h new/source/interpret.h
--- old/source/interpret.h
+++ new/source/interpret.h
@@ -35,10 +35,13 @@
#define STACK_SIZE 1024 /* Maximum stack size */
#define MAX_SYM_LEN 100 /* Max. symbol name length */
#define MACRO_EVENT_MARKER 2 /* Special value for the send_event field of
events passed to action routines. Tells
them that they were called from a macro */
+#define PROGRAM_SIZE 4096 /* Maximum program size */
+#define LOOP_STACK_SIZE 256 /* (Approx.) Number of break/continue stmts
+ allowed per program */
enum symTypes {CONST_SYM, GLOBAL_SYM, LOCAL_SYM, ARG_SYM, PROC_VALUE_SYM,
C_FUNCTION_SYM, MACRO_FUNCTION_SYM, ACTION_ROUTINE_SYM};
#define N_OPS 43
enum operations {OP_RETURN_NO_VAL, OP_RETURN, OP_PUSH_SYM, OP_DUP, OP_ADD,
@@ -116,10 +119,19 @@ typedef struct {
Inst *pc;
WindowInfo *runWindow;
WindowInfo *focusWindow;
} RestartData;
+/* state of the accumulator (aka compiler) */
+typedef struct AccumulatorDataTag {
+ Symbol *localSymList;
+ Inst prog[PROGRAM_SIZE];
+ Inst *progP;
+ Inst *loopStack[LOOP_STACK_SIZE];
+ Inst **loopStackPtr;
+} AccumulatorData;
+
void InitMacroGlobals(void);
SparseArrayEntry *arrayIterateFirst(DataValue *theArray);
SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator);
SparseArrayEntry *ArrayNew(void);
@@ -130,22 +142,22 @@ unsigned ArraySize(DataValue *theArray);
Boolean ArrayGet(DataValue* theArray, char* keyStr, DataValue* theValue);
int ArrayCopy(DataValue *dstArray, DataValue *srcArray);
/* Routines for creating a program, (accumulated beginning with
BeginCreatingProgram and returned via FinishCreatingProgram) */
-void BeginCreatingProgram(void);
+void BeginCreatingProgram(AccumulatorData *acc);
int AddOp(int op, char **msg);
int AddSym(Symbol *sym, char **msg);
int AddImmediate(int value, char **msg);
int AddBranchOffset(Inst *to, char **msg);
Inst *GetPC(void);
Symbol *InstallIteratorSymbol(void);
Symbol *LookupStringConstSymbol(const char *value);
Symbol *InstallStringConstSymbol(const char *str);
Symbol *LookupSymbol(const char *name);
Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value);
-Program *FinishCreatingProgram(void);
+Program *FinishCreatingProgram(AccumulatorData *acc);
void SwapCode(Inst *start, Inst *boundary, Inst *end);
void StartLoopAddrList(void);
int AddBreakAddr(Inst *addr);
int AddContinueAddr(Inst *addr);
void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr);
diff --quilt old/source/macro.c new/source/macro.c
--- old/source/macro.c
+++ new/source/macro.c
@@ -743,11 +743,11 @@ void Replay(WindowInfo *window)
if (ReplayMacro != NULL &&
ReplayMacro[0] != 0 &&
window->macroCmdData == NULL) {
/* Parse the replay macro (it's stored in text form) and compile it
into
an executable program "prog" */
- prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt);
+ prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt, False);
if (prog == NULL) {
fprintf(stderr,
"NEdit internal error, learn/replay macro syntax error: %s\n",
errMsg);
return;
@@ -838,124 +838,39 @@ int CheckMacroString(Widget dialogParent
** returns a pointer to the error location in the string.
*/
static int readCheckMacroString(Widget dialogParent, char *string,
WindowInfo *runWindow, const char *errIn, char **errPos)
{
- char *stoppedAt, *inPtr, *namePtr, *errMsg;
- char subrName[MAX_SYM_LEN];
+ char *stoppedAt, *errMsg;
Program *prog;
- Symbol *sym;
- DataValue subrPtr;
- Stack* progStack = (Stack*) XtMalloc(sizeof(Stack));
- progStack->top = NULL;
- progStack->size = 0;
- inPtr = string;
- while (*inPtr != '\0') {
-
- /* skip over white space and comments */
- while (*inPtr==' ' || *inPtr=='\t' || *inPtr=='\n'|| *inPtr=='#') {
- if (*inPtr == '#')
- while (*inPtr != '\n' && *inPtr != '\0') inPtr++;
- else
- inPtr++;
- }
- if (*inPtr == '\0')
- break;
-
- /* look for define keyword, and compile and store defined routines */
- if (!strncmp(inPtr, "define", 6) && (inPtr[6]==' ' || inPtr[6]=='\t')) {
- inPtr += 6;
- inPtr += strspn(inPtr, " \t\n");
- namePtr = subrName;
- while ((namePtr < &subrName[MAX_SYM_LEN - 1])
- && (isalnum((unsigned char)*inPtr) || *inPtr == '_')) {
- *namePtr++ = *inPtr++;
- }
- *namePtr = '\0';
- if (isalnum((unsigned char)*inPtr) || *inPtr == '_') {
- return ParseError(dialogParent, string, inPtr, errIn,
- "subroutine name too long");
- }
- inPtr += strspn(inPtr, " \t\n");
- if (*inPtr != '{') {
- if (errPos != NULL) *errPos = stoppedAt;
- return ParseError(dialogParent, string, inPtr,
- errIn, "expected '{'");
- }
- prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
- if (prog == NULL) {
- if (errPos != NULL) *errPos = stoppedAt;
- return ParseError(dialogParent, string, stoppedAt,
- errIn, errMsg);
- }
- if (runWindow != NULL) {
- sym = LookupSymbol(subrName);
- if (sym == NULL) {
- subrPtr.val.prog = prog;
- subrPtr.tag = NO_TAG;
- sym = InstallSymbol(subrName, MACRO_FUNCTION_SYM, subrPtr);
- } else {
- if (sym->type == MACRO_FUNCTION_SYM)
- FreeProgram(sym->value.val.prog);
- else
- sym->type = MACRO_FUNCTION_SYM;
- sym->value.val.prog = prog;
- }
- }
- inPtr = stoppedAt;
-
- /* Parse and execute immediate (outside of any define) macro commands
- and WAIT for them to finish executing before proceeding. Note that
- the code below is not perfect. If you interleave code blocks with
- definitions in a file which is loaded from another macro file, it
- will probably run the code blocks in reverse order! */
- } else {
- prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
- if (prog == NULL) {
- if (errPos != NULL) {
- *errPos = stoppedAt;
- }
-
- return ParseError(dialogParent, string, stoppedAt,
- errIn, errMsg);
- }
-
- if (runWindow != NULL) {
- XEvent nextEvent;
- if (runWindow->macroCmdData == NULL) {
- runMacro(runWindow, prog);
- while (runWindow->macroCmdData != NULL) {
- XtAppNextEvent(XtWidgetToApplicationContext(
- runWindow->shell), &nextEvent);
- ServerDispatchEvent(&nextEvent);
- }
- } else {
- /* If we come here this means that the string was parsed
- from within another macro via load_macro_file(). In
- this case, plain code segments outside of define
- blocks are rolled into one Program each and put on
- the stack. At the end, the stack is unrolled, so the
- plain Programs would be executed in the wrong order.
-
- So we don't hand the Programs over to the interpreter
- just yet (via RunMacroAsSubrCall()), but put it on a
- stack of our own, reversing order once again. */
- Push(progStack, (void*) prog);
- }
- }
- inPtr = stoppedAt;
- }
+ prog = ParseMacro(string, &errMsg, &stoppedAt, True);
+ if (prog == NULL) {
+ if (errPos != NULL) {
+ *errPos = stoppedAt;
+ }
+ return ParseError(dialogParent, string, stoppedAt, errIn, errMsg);
}
- /* Unroll reversal stack for macros loaded from macros. */
- while (NULL != (prog = (Program*) Pop(progStack))) {
- RunMacroAsSubrCall(prog);
+ if (runWindow != NULL) {
+ XEvent nextEvent;
+ if (runWindow->macroCmdData == NULL) {
+ runMacro(runWindow, prog);
+ while (runWindow->macroCmdData != NULL) {
+ XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
+ &nextEvent);
+ ServerDispatchEvent(&nextEvent);
+ }
+ /* FreeProgram(prog)? */
+ }
+ else {
+ RunMacroAsSubrCall(prog);
+ }
+ }
+ else {
+ FreeProgram(prog);
}
-
- /* This stack is empty, so just free it without checking the members. */
- XtFree((char*) progStack);
return True;
}
/*
@@ -1211,11 +1126,11 @@ void DoMacro(WindowInfo *window, const c
strncpy(tMacro, macro, macroLen);
tMacro[macroLen] = '\n';
tMacro[macroLen+1] = '\0';
/* Parse the macro and report errors if it fails */
- prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
+ prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
if (prog == NULL) {
ParseError(window->shell, tMacro, stoppedAt, errInName, errMsg);
XtFree(tMacro);
return;
}
@@ -1475,11 +1390,11 @@ selEnd += $text_length - startLength\n}\
sprintf(loopedCmd, loopMacro, command);
else
sprintf(loopedCmd, loopMacro, how, command);
/* Parse the resulting macro into an executable program "prog" */
- prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt);
+ prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt, False);
if (prog == NULL) {
fprintf(stderr, "NEdit internal error, repeat macro syntax wrong: %s\n",
errMsg);
return;
}
diff --quilt old/source/nedit.c new/source/nedit.c
--- old/source/nedit.c
+++ new/source/nedit.c
@@ -830,11 +830,11 @@ static int checkDoMacroArg(const char *m
strncpy(tMacro, macro, macroLen);
tMacro[macroLen] = '\n';
tMacro[macroLen+1] = '\0';
/* Do a test parse */
- prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
+ prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
XtFree(tMacro);
if (prog == NULL) {
ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
return False;
}
diff --quilt old/source/parse.h new/source/parse.h
--- old/source/parse.h
+++ new/source/parse.h
@@ -28,8 +28,8 @@
#ifndef NEDIT_PARSE_H_INCLUDED
#define NEDIT_PARSE_H_INCLUDED
#include "interpret.h"
-Program *ParseMacro(char *expr, char **msg, char **stoppedAt);
+Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine);
#endif /* NEDIT_PARSE_H_INCLUDED */
diff --quilt old/source/parse.y new/source/parse.y
--- old/source/parse.y
+++ new/source/parse.y
@@ -50,20 +50,24 @@ static Symbol *matchesActionRoutine(char
static char *ErrMsg;
static char *InPtr;
extern Inst *LoopStack[]; /* addresses of break, cont stmts */
extern Inst **LoopStackPtr; /* to fill at the end of a loop */
+static int inDefine;
+
%}
%union {
Symbol *sym;
Inst *inst;
int nArgs;
+ AccumulatorData *acc;
}
%token <sym> NUMBER STRING SYMBOL
%token DELETE ARG_LOOKUP
%token IF WHILE ELSE FOR BREAK CONTINUE RETURN
+%token <acc> DEFINE
%type <nArgs> arglist
%type <inst> cond comastmts for while else and or arrayexpr
%type <sym> evalsym
%nonassoc IF_NO_ELSE
@@ -99,18 +103,42 @@ program: blank stmts {
}
| error {
return 1;
}
;
-block: '{' blank stmts '}' blank
+blockwb: '{' blank stmts '}' blank
| '{' blank '}' blank
+ ;
+block: blockwb
| stmt
;
stmts: stmt
| stmts stmt
;
-stmt: simpstmt '\n' blank
+stmt: DEFINE {
+ /* bail out early, in case of unallowed "define" */
+ if (inDefine) {
+ yyerror("macro definitions not allowed here"); YYERROR;
+ }
+ }
+ blank SYMBOL {
+ $1 = (AccumulatorData *)XtMalloc(sizeof(AccumulatorData));
+ BeginCreatingProgram($1);
+ inDefine = True;
+ }
+ blank blockwb {
+ if ($4->type == MACRO_FUNCTION_SYM) {
+ FreeProgram($4->value.val.prog);
+ }
+ else {
+ $4->type = MACRO_FUNCTION_SYM;
+ }
+ $4->value.val.prog = FinishCreatingProgram($1);
+ XtFree((char *)$1);
+ inDefine = False;
+ }
+ | simpstmt '\n' blank
| IF '(' cond ')' blank block %prec IF_NO_ELSE {
SET_BR_OFF($3, GetPC());
}
| IF '(' cond ')' blank block else blank block %prec ELSE {
SET_BR_OFF($3, ($7+1)); SET_BR_OFF($7, GetPC());
@@ -449,29 +477,35 @@ blank: /* nothing */
** executed using ExecuteProgram. Returns program on success, or NULL
** on failure. If the command failed, the error message is returned
** as a pointer to a static string in msg, and the length of the string up
** to where parsing failed in stoppedAt.
*/
-Program *ParseMacro(char *expr, char **msg, char **stoppedAt)
+Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine)
{
Program *prog;
+ AccumulatorData *acc = (AccumulatorData *)XtMalloc(sizeof(*acc));
- BeginCreatingProgram();
+ BeginCreatingProgram(acc);
+
+ /* wether we allow the "define" keyword */
+ inDefine = !allowDefine;
/* call yyparse to parse the string and check for success. If the parse
failed, return the error message and string index (the grammar aborts
parsing at the first error) */
InPtr = expr;
if (yyparse()) {
*msg = ErrMsg;
*stoppedAt = InPtr;
- FreeProgram(FinishCreatingProgram());
+ FreeProgram(FinishCreatingProgram(acc));
+ XtFree((char *)acc);
return NULL;
}
/* get the newly created program */
- prog = FinishCreatingProgram();
+ prog = FinishCreatingProgram(acc);
+ XtFree((char *)acc);
/* parse succeeded */
*msg = "";
*stoppedAt = InPtr;
return prog;
@@ -525,13 +559,12 @@ static int yylex(void)
if ((yylval.sym=LookupSymbol(name)) == NULL)
yylval.sym = InstallSymbol(name, CONST_SYM, value);
return NUMBER;
}
- /* process symbol tokens. "define" is a special case not handled
- by this parser, considered end of input. Another special case
- is action routine names which are allowed to contain '-' despite
+ /* process symbol tokens. A special case are action
+ routine names which are allowed to contain '-' despite
the ambiguity, handled in matchesActionRoutine. */
if (isalpha((unsigned char)*InPtr) || *InPtr == '$') {
if ((s=matchesActionRoutine(&InPtr)) == NULL) {
char symName[MAX_SYM_LEN+1], *p = symName;
*p++ = *InPtr++;
@@ -550,14 +583,11 @@ static int yylex(void)
if (!strcmp(symName, "continue")) return CONTINUE;
if (!strcmp(symName, "return")) return RETURN;
if (!strcmp(symName, "in")) return IN;
if (!strcmp(symName, "$args")) return ARG_LOOKUP;
if (!strcmp(symName, "delete") && follow_non_whitespace('(',
SYMBOL, DELETE) == DELETE) return DELETE;
- if (!strcmp(symName, "define")) {
- InPtr -= 6;
- return 0;
- }
+ if (!strcmp(symName, "define")) return DEFINE;
if ((s=LookupSymbol(symName)) == NULL) {
s = InstallSymbol(symName, symName[0]=='$' ?
(((symName[1] > '0' && symName[1] <= '9') &&
symName[2] == 0) ?
ARG_SYM : GLOBAL_SYM) : LOCAL_SYM, value);
s->value.tag = NO_TAG;
diff --quilt old/source/smartIndent.c new/source/smartIndent.c
--- old/source/smartIndent.c
+++ new/source/smartIndent.c
@@ -745,21 +745,21 @@ void BeginSmartIndent(WindowInfo *window
/* Compile the newline and modify macros and attach them to the window */
winData = (windowSmartIndentData *)XtMalloc(sizeof(windowSmartIndentData));
winData->inNewLineMacro = 0;
winData->inModMacro = 0;
winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
- &stoppedAt);
+ &stoppedAt, False);
if (winData->newlineMacro == NULL) {
ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
"newline macro", errMsg);
return;
}
if (indentMacros->modMacro == NULL)
winData->modMacro = NULL;
else {
winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
- &stoppedAt);
+ &stoppedAt, False);
if (winData->modMacro == NULL) {
ParseError(window->shell, indentMacros->modMacro, stoppedAt,
"smart indent modify macro", errMsg);
return;
}
@@ -1435,11 +1435,11 @@ static int checkSmartIndentDialogData(vo
"Newline macro required", "OK");
return False;
}
widgetText =
ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
- prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
+ prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
if (prog == NULL) {
ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
"newline macro", errMsg);
XmTextSetInsertionPosition(SmartIndentDialog.newlineMacro,
stoppedAt - widgetText);
@@ -1451,11 +1451,11 @@ static int checkSmartIndentDialogData(vo
FreeProgram(prog);
/* Test compile the modify macro */
if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
- prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
+ prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
if (prog == NULL) {
ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
"modify macro", errMsg);
XmTextSetInsertionPosition(SmartIndentDialog.modMacro,
stoppedAt - widgetText);
diff --quilt old/source/userCmds.c new/source/userCmds.c
--- old/source/userCmds.c
+++ new/source/userCmds.c
@@ -2078,11 +2078,11 @@ static int checkMacro(userCmdDialog *ucd
static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
{
Program *prog;
char *errMsg, *stoppedAt;
- prog = ParseMacro(macro, &errMsg, &stoppedAt);
+ prog = ParseMacro(macro, &errMsg, &stoppedAt, False);
if (prog == NULL) {
if (errorParent != NULL) {
ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
@@ -3017,11 +3017,11 @@ static char *copyMacroToEnd(char **inPtr
ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
return NULL;
}
/* Parse the input */
- prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
+ prog = ParseMacro(*inPtr, &errMsg, &stoppedAt, False);
if (prog == NULL) {
ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
return NULL;
}
FreeProgram(prog);
--
NEdit Develop mailing list - [email protected]
http://www.nedit.org/mailman/listinfo/develop