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")

ChangeLog:
 v6:
   * never asign to a $n variable in yacc
 v5:
   * fix memcpy sizeof arguments
 v4:
   * collapse run program code into runMacro() call
 v3:
   * add missing OP_RETURN_NO_VAL after define
 v2:
   * make the define statement special, prevents recursion of the define
     keyword
   * need to PromoteToGlobal() the new function symbol
   * prevent overriding of non macro functions

---

 source/interpret.c   |   24 ++++++---
 source/interpret.h   |   16 +++++-
 source/macro.c       |  135 ++++++++-------------------------------------------
 source/nedit.c       |    2 
 source/parse.h       |    2 
 source/parse.y       |   80 ++++++++++++++++++++++++------
 source/smartIndent.c |    8 +--
 source/userCmds.c    |    4 -
 8 files changed, 128 insertions(+), 143 deletions(-)

diff --quilt old/source/interpret.c new/source/interpret.c
--- old/source/interpret.c
+++ new/source/interpret.c
@@ -62,10 +62,7 @@ static const char CVSID[] = "$Id: interp
 #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 */
@@ -274,8 +271,15 @@ 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(*Prog) * PROGRAM_SIZE);
+    acc->progP = ProgP;
+    memcpy(acc->loopStack, LoopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
+    acc->loopStackPtr = LoopStackPtr;
+
     LocalSymList = NULL;
     ProgP = Prog;
     LoopStackPtr = LoopStack;
@@ -286,7 +290,7 @@ void BeginCreatingProgram(void)
 ** 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;
@@ -297,7 +301,6 @@ Program *FinishCreatingProgram(void)
     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. */
@@ -306,6 +309,13 @@ Program *FinishCreatingProgram(void)
     
     DISASM(newProg->code, ProgP - Prog);
     
+    /* restore state */
+    LocalSymList = acc->localSymList;
+    memcpy(Prog, acc->prog, sizeof(*Prog) * PROGRAM_SIZE);
+    ProgP = acc->progP;
+    memcpy(LoopStack, acc->loopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
+    LoopStackPtr = acc->loopStackPtr;
+
     return newProg;
 }
 
diff --quilt old/source/interpret.h new/source/interpret.h
--- old/source/interpret.h
+++ new/source/interpret.h
@@ -36,6 +36,9 @@
 #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};
@@ -117,6 +120,15 @@ typedef struct {
     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);
@@ -131,7 +143,7 @@ int ArrayCopy(DataValue *dstArray, DataV
 
 /* 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);
@@ -142,7 +154,7 @@ Symbol *LookupStringConstSymbol(const ch
 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);
diff --quilt old/source/macro.c new/source/macro.c
--- old/source/macro.c
+++ new/source/macro.c
@@ -745,7 +745,7 @@ void Replay(WindowInfo *window)
             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",
@@ -840,120 +840,31 @@ int CheckMacroString(Widget dialogParent
 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;
+        /* runMacro() is responsable for freeing prog */
+        runMacro(runWindow, prog);
+        while (runWindow->macroCmdData != NULL) {
+            XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
+                    &nextEvent);
+            ServerDispatchEvent(&nextEvent);
+        }
+    }
+    else {
+        /* we are in 'parse only' mode, therefore release the prog */
+        FreeProgram(prog);
     }
-
-    /*  This stack is empty, so just free it without checking the members.  */
-    XtFree((char*) progStack);
 
     return True;
 }
@@ -1213,7 +1124,7 @@ void DoMacro(WindowInfo *window, const c
     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);
@@ -1477,7 +1388,7 @@ selEnd += $text_length - startLength\n}\
        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);
diff --quilt old/source/nedit.c new/source/nedit.c
--- old/source/nedit.c
+++ new/source/nedit.c
@@ -832,7 +832,7 @@ static int checkDoMacroArg(const char *m
     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);
diff --quilt old/source/parse.h new/source/parse.h
--- old/source/parse.h
+++ new/source/parse.h
@@ -30,6 +30,6 @@
 
 #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
@@ -52,16 +52,22 @@ static char *InPtr;
 extern Inst *LoopStack[]; /* addresses of break, cont stmts */
 extern Inst **LoopStackPtr;  /*  to fill at the end of a loop */
 
+static int AllowDefine;
+
 %}
 
 %union {
     Symbol *sym;
     Inst *inst;
     int nArgs;
+    struct {
+        AccumulatorData *acc;
+        Symbol *sym;
+    } define;
 }
 %token <sym> NUMBER STRING SYMBOL
 %token DELETE ARG_LOOKUP
-%token IF WHILE ELSE FOR BREAK CONTINUE RETURN
+%token IF WHILE ELSE FOR BREAK CONTINUE RETURN DEFINE
 %type <nArgs> arglist
 %type <inst> cond comastmts for while else and or arrayexpr
 %type <sym> evalsym
@@ -88,7 +94,7 @@ extern Inst **LoopStackPtr;  /*  to fill
 
 %%      /* Rules */
 
-program:    blank stmts {
+program:    blank allstmts {
                 ADD_OP(OP_RETURN_NO_VAL); return 0;
             }
             | blank '{' blank stmts '}' {
@@ -101,13 +107,57 @@ program:    blank stmts {
                 return 1;
             }
             ;
-block:      '{' blank stmts '}' blank
+blockwb:    '{' blank stmts '}' blank
             | '{' blank '}' blank
+            ;
+block:      blockwb
             | stmt
             ;
 stmts:      stmt
             | stmts stmt
             ;
+allstmts:   allstmt
+            | allstmts allstmt
+            ;
+allstmt:    stmt
+            | define
+            ;
+define:     DEFINE {
+                /* bail out early, in case of unallowed "define" */
+                if (!AllowDefine) {
+                    yyerror("macro definitions not allowed"); YYERROR;
+                }
+            }
+                blank SYMBOL {
+                    $<define>$.acc = (AccumulatorData *)
+                            XtMalloc(sizeof(AccumulatorData));
+                    /* we can't really be sure, that we not overwrite any
+                    ** wrong symbol
+                    **
+                    ** we should only overwrite installed MACRO_FUNCTION_SYM
+                    ** and this is questionable.
+                    */
+                    if ($4->type == MACRO_FUNCTION_SYM) {
+                        FreeProgram($4->value.val.prog);
+                    }
+                    else if ($4->type == LOCAL_SYM ||
+                            $4->type == GLOBAL_SYM) {
+                        /* newly created sym, or we overwrite a local sym */;
+                    } else {
+                        yyerror("try to override built-in subroutine"); 
YYERROR;
+                    }
+                    $<define>$.sym = PromoteToGlobal($4);
+                    BeginCreatingProgram($<define>$.acc);
+            }
+                blank blockwb {
+                    ADD_OP(OP_RETURN_NO_VAL);
+                    Program *prog = FinishCreatingProgram($<define>5.acc);
+                    XtFree((char *)$<define>5.acc);
+                    $<define>5.sym->type = MACRO_FUNCTION_SYM;
+                    $<define>5.sym->value.tag = NO_TAG;
+                    $<define>5.sym->value.val.prog = prog;
+            }
+            ;
 stmt:       simpstmt '\n' blank
             | IF '(' cond ')' blank block %prec IF_NO_ELSE {
                 SET_BR_OFF($3, GetPC());
@@ -451,11 +501,15 @@ blank:  /* nothing */
 ** 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(acc);
 
-    BeginCreatingProgram();
+    /* whether we allow the "define" keyword */
+    AllowDefine = 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
@@ -464,12 +518,14 @@ Program *ParseMacro(char *expr, char **m
     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 = "";
@@ -527,9 +583,8 @@ static int yylex(void)
         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) {
@@ -552,10 +607,7 @@ static int yylex(void)
             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) ?
diff --quilt old/source/smartIndent.c new/source/smartIndent.c
--- old/source/smartIndent.c
+++ new/source/smartIndent.c
@@ -747,7 +747,7 @@ void BeginSmartIndent(WindowInfo *window
     winData->inNewLineMacro = 0;
     winData->inModMacro = 0;
     winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
-           &stoppedAt);
+           &stoppedAt, False);
     if (winData->newlineMacro == NULL) {
         XtFree((char *)winData);
        ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
@@ -758,7 +758,7 @@ void BeginSmartIndent(WindowInfo *window
        winData->modMacro = NULL;
     else {
        winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
-               &stoppedAt);
+               &stoppedAt, False);
        if (winData->modMacro == NULL) {
             FreeProgram(winData->newlineMacro);
             XtFree((char *)winData);
@@ -1440,7 +1440,7 @@ static int checkSmartIndentDialogData(vo
     }
 
     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);
@@ -1456,7 +1456,7 @@ static int checkSmartIndentDialogData(vo
     /* 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);
diff --quilt old/source/userCmds.c new/source/userCmds.c
--- old/source/userCmds.c
+++ new/source/userCmds.c
@@ -2080,7 +2080,7 @@ static int checkMacroText(char *macro, W
     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);
@@ -3019,7 +3019,7 @@ static char *copyMacroToEnd(char **inPtr
     }
 
     /* 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;
-- 
NEdit Develop mailing list - [email protected]
http://www.nedit.org/mailman/listinfo/develop

Reply via email to