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:
v7:
* avoid intra-actions
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 | 84 ++++++++++++++++++++++++++-----
source/smartIndent.c | 8 +--
source/userCmds.c | 4 -
8 files changed, 132 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,19 +52,26 @@ 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
+%type <define> definesym
%nonassoc IF_NO_ELSE
%nonassoc ELSE
@@ -88,7 +95,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 +108,60 @@ 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: definekw blank definesym blank blockwb {
+ ADD_OP(OP_RETURN_NO_VAL);
+ Program *prog = FinishCreatingProgram($3.acc);
+ XtFree((char *)$3.acc);
+ $3.sym->type = MACRO_FUNCTION_SYM;
+ $3.sym->value.tag = NO_TAG;
+ $3.sym->value.val.prog = prog;
+ }
+ ;
+definekw: DEFINE {
+ /* bail out early, in case of unallowed "define" */
+ if (!AllowDefine) {
+ yyerror("macro definitions not allowed"); YYERROR;
+ }
+ }
+ ;
+definesym: SYMBOL {
+ $$.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 ($1->type == MACRO_FUNCTION_SYM) {
+ FreeProgram($1->value.val.prog);
+ }
+ else if ($1->type == LOCAL_SYM ||
+ $1->type == GLOBAL_SYM) {
+ /* newly created sym, or we overwrite a local sym */;
+ } else {
+ yyerror("try to override built-in subroutine"); YYERROR;
+ }
+ $$.sym = PromoteToGlobal($1);
+ BeginCreatingProgram($$.acc);
+ }
+ ;
+
stmt: simpstmt '\n' blank
| IF '(' cond ')' blank block %prec IF_NO_ELSE {
SET_BR_OFF($3, GetPC());
@@ -451,11 +505,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();
+ BeginCreatingProgram(acc);
+
+ /* 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 +522,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 +587,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 +611,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