1 From 843e5c23eb9f637981697621276d5b4aed5a8b0e Mon Sep 17 00:00:00 2001
2 From: Bert Wesarg <bert.wesarg@googlemail.com>
3 Date: Tue, 2 Sep 2008 10:36:47 +0200
4 Subject: [PATCH v3] parse "define" in the parser
6 This removes the ugly parsing of the "define" keyword. It solves this by
7 saving and restoring the state of the program accumulation. This solves
8 a long standing problam, that parts between "define" definition aren't
9 in the same scope, i.e. this works now:
20 * add missing OP_RETURN_NO_VAL after define
22 * make the define statement special, prevents recursion of the define
24 * need to PromoteToGlobal() the new function symbol
25 * prevent overriding of non macro functions
29 source/interpret.c | 24 +++++---
30 source/interpret.h | 16 ++++-
31 source/macro.c | 147 +++++++++------------------------------------------
34 source/parse.y | 78 ++++++++++++++++++++++-----
35 source/smartIndent.c | 8 +-
36 source/userCmds.c | 4 -
37 8 files changed, 131 insertions(+), 150 deletions(-)
39 diff --quilt old/source/interpret.c new/source/interpret.c
40 --- old/source/interpret.c
41 +++ new/source/interpret.c
42 @@ -60,14 +60,11 @@ static const char CVSID[] = "$Id: interp
48 -#define PROGRAM_SIZE 4096 /* Maximum program size */
49 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
50 -#define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
51 - allowed per program */
52 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
53 allowed to execute before preempting and
54 returning to allow other things to run */
56 /* Temporary markers placed in a branch address location to designate
57 @@ -237,42 +234,55 @@ void InitMacroGlobals(void)
60 ** Start collecting instructions for a program. Clears the program
61 ** and the symbol table.
63 -void BeginCreatingProgram(void)
65 +void BeginCreatingProgram(AccumulatorData *acc)
68 + acc->localSymList = LocalSymList;
69 + memcpy(acc->prog, Prog, sizeof(Inst) * PROGRAM_SIZE);
71 + memcpy(acc->loopStack, LoopStack, sizeof(Inst) * LOOP_STACK_SIZE);
72 + acc->loopStackPtr = LoopStackPtr;
76 LoopStackPtr = LoopStack;
80 ** Finish up the program under construction, and return it (code and
81 ** symbol table) as a package that ExecuteMacro can execute. This
82 ** program must be freed with FreeProgram.
84 -Program *FinishCreatingProgram(void)
85 +Program *FinishCreatingProgram(AccumulatorData *acc)
88 int progLen, fpOffset = 0;
91 newProg = (Program *)XtMalloc(sizeof(Program));
92 progLen = ((char *)ProgP) - ((char *)Prog);
93 newProg->code = (Inst *)XtMalloc(progLen);
94 memcpy(newProg->code, Prog, progLen);
95 newProg->localSymList = LocalSymList;
96 - LocalSymList = NULL;
98 /* Local variables' values are stored on the stack. Here we assign
99 frame pointer offsets to them. */
100 for (s = newProg->localSymList; s != NULL; s = s->next)
101 s->value.val.n = fpOffset++;
103 DISASM(newProg->code, ProgP - Prog);
105 + /* restore state */
106 + LocalSymList = acc->localSymList;
107 + memcpy(Prog, acc->prog, sizeof(Inst) * PROGRAM_SIZE);
108 + ProgP = acc->progP;
109 + memcpy(LoopStack, acc->loopStack, sizeof(Inst) * LOOP_STACK_SIZE);
110 + LoopStackPtr = acc->loopStackPtr;
115 void FreeProgram(Program *prog)
117 diff --quilt old/source/interpret.h new/source/interpret.h
118 --- old/source/interpret.h
119 +++ new/source/interpret.h
121 #define STACK_SIZE 1024 /* Maximum stack size */
122 #define MAX_SYM_LEN 100 /* Max. symbol name length */
123 #define MACRO_EVENT_MARKER 2 /* Special value for the send_event field of
124 events passed to action routines. Tells
125 them that they were called from a macro */
126 +#define PROGRAM_SIZE 4096 /* Maximum program size */
127 +#define LOOP_STACK_SIZE 256 /* (Approx.) Number of break/continue stmts
128 + allowed per program */
130 enum symTypes {CONST_SYM, GLOBAL_SYM, LOCAL_SYM, ARG_SYM, PROC_VALUE_SYM,
131 C_FUNCTION_SYM, MACRO_FUNCTION_SYM, ACTION_ROUTINE_SYM};
134 @@ -114,10 +117,19 @@ typedef struct {
136 WindowInfo *runWindow;
137 WindowInfo *focusWindow;
140 +/* state of the accumulator (aka compiler) */
141 +typedef struct AccumulatorDataTag {
142 + Symbol *localSymList;
143 + Inst prog[PROGRAM_SIZE];
145 + Inst *loopStack[LOOP_STACK_SIZE];
146 + Inst **loopStackPtr;
149 void InitMacroGlobals(void);
151 SparseArrayEntry *arrayIterateFirst(DataValue *theArray);
152 SparseArrayEntry *arrayIterateNext(SparseArrayEntry *iterator);
153 SparseArrayEntry *ArrayNew(void);
154 @@ -128,22 +140,22 @@ unsigned ArraySize(DataValue *theArray);
155 Boolean ArrayGet(DataValue* theArray, char* keyStr, DataValue* theValue);
156 int ArrayCopy(DataValue *dstArray, DataValue *srcArray);
158 /* Routines for creating a program, (accumulated beginning with
159 BeginCreatingProgram and returned via FinishCreatingProgram) */
160 -void BeginCreatingProgram(void);
161 +void BeginCreatingProgram(AccumulatorData *acc);
162 int AddOp(int op, char **msg);
163 int AddSym(Symbol *sym, char **msg);
164 int AddImmediate(int value, char **msg);
165 int AddBranchOffset(Inst *to, char **msg);
167 Symbol *InstallIteratorSymbol(void);
168 Symbol *LookupStringConstSymbol(const char *value);
169 Symbol *InstallStringConstSymbol(const char *str);
170 Symbol *LookupSymbol(const char *name);
171 Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value);
172 -Program *FinishCreatingProgram(void);
173 +Program *FinishCreatingProgram(AccumulatorData *acc);
174 void SwapCode(Inst *start, Inst *boundary, Inst *end);
175 void StartLoopAddrList(void);
176 int AddBreakAddr(Inst *addr);
177 int AddContinueAddr(Inst *addr);
178 void FillLoopAddrs(Inst *breakAddr, Inst *continueAddr);
179 diff --quilt old/source/macro.c new/source/macro.c
180 --- old/source/macro.c
181 +++ new/source/macro.c
182 @@ -754,11 +754,11 @@ void Replay(WindowInfo *window)
183 if (ReplayMacro != NULL &&
184 ReplayMacro[0] != 0 &&
185 window->macroCmdData == NULL) {
186 /* Parse the replay macro (it's stored in text form) and compile it into
187 an executable program "prog" */
188 - prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt);
189 + prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt, False);
192 "NEdit internal error, learn/replay macro syntax error: %s\n",
195 @@ -849,133 +849,40 @@ int CheckMacroString(Widget dialogParent
196 ** returns a pointer to the error location in the string.
198 static int readCheckMacroString(Widget dialogParent, char *string,
199 WindowInfo *runWindow, const char *errIn, char **errPos)
201 - char *stoppedAt, *inPtr, *namePtr, *errMsg;
202 - char subrName[MAX_SYM_LEN];
203 + char *stoppedAt, *errMsg;
207 - Stack* progStack = (Stack*) XtMalloc(sizeof(Stack));
208 - progStack->top = NULL;
209 - progStack->size = 0;
212 - while (*inPtr != '\0') {
214 - /* skip over white space and comments */
215 - while (*inPtr==' ' || *inPtr=='\t' || *inPtr=='\n'|| *inPtr=='#') {
217 - while (*inPtr != '\n' && *inPtr != '\0') inPtr++;
221 - if (*inPtr == '\0')
224 - /* look for define keyword, and compile and store defined routines */
225 - if (!strncmp(inPtr, "define", 6) && (inPtr[6]==' ' || inPtr[6]=='\t')) {
227 - inPtr += strspn(inPtr, " \t\n");
228 - namePtr = subrName;
229 - while ((namePtr < &subrName[MAX_SYM_LEN - 1])
230 - && (isalnum((unsigned char)*inPtr) || *inPtr == '_')) {
231 - *namePtr++ = *inPtr++;
234 - if (isalnum((unsigned char)*inPtr) || *inPtr == '_') {
235 - return ParseError(dialogParent, string, inPtr, errIn,
236 - "subroutine name too long");
238 - inPtr += strspn(inPtr, " \t\n");
239 - if (*inPtr != '{') {
240 - if (errPos != NULL) *errPos = stoppedAt;
241 - return ParseError(dialogParent, string, inPtr,
242 - errIn, "expected '{'");
244 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
245 - if (prog == NULL) {
246 - if (errPos != NULL) *errPos = stoppedAt;
247 - return ParseError(dialogParent, string, stoppedAt,
250 - if (runWindow != NULL) {
251 - sym = LookupSymbol(subrName);
253 - subrPtr.val.prog = prog;
254 - subrPtr.tag = NO_TAG;
255 - sym = InstallSymbol(subrName, MACRO_FUNCTION_SYM, subrPtr);
257 - if (sym->type == MACRO_FUNCTION_SYM)
258 - /* its unsafe to free the old program,
259 - ** maybe it is currently in use
261 - /* FreeProgram(sym->value.val.prog) */;
263 - sym->type = MACRO_FUNCTION_SYM;
264 - sym->value.val.prog = prog;
269 - /* Parse and execute immediate (outside of any define) macro commands
270 - and WAIT for them to finish executing before proceeding. Note that
271 - the code below is not perfect. If you interleave code blocks with
272 - definitions in a file which is loaded from another macro file, it
273 - will probably run the code blocks in reverse order! */
275 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
276 - if (prog == NULL) {
277 - if (errPos != NULL) {
278 - *errPos = stoppedAt;
281 - return ParseError(dialogParent, string, stoppedAt,
285 - if (runWindow != NULL) {
287 - if (runWindow->macroCmdData == NULL) {
288 - /* runMacro() is responsable for freeing prog */
289 - runMacro(runWindow, prog);
290 - while (runWindow->macroCmdData != NULL) {
291 - XtAppNextEvent(XtWidgetToApplicationContext(
292 - runWindow->shell), &nextEvent);
293 - ServerDispatchEvent(&nextEvent);
296 - /* If we come here this means that the string was parsed
297 - from within another macro via load_macro_file(). In
298 - this case, plain code segments outside of define
299 - blocks are rolled into one Program each and put on
300 - the stack. At the end, the stack is unrolled, so the
301 - plain Programs would be executed in the wrong order.
303 - So we don't hand the Programs over to the interpreter
304 - just yet (via RunMacroAsSubrCall()), but put it on a
305 - stack of our own, reversing order once again. */
306 - Push(progStack, (void*) prog);
312 - /* we are in 'parse only' mode, therefore release the prog */
313 - if (runWindow == NULL) {
315 + prog = ParseMacro(string, &errMsg, &stoppedAt, True);
316 + if (prog == NULL) {
317 + if (errPos != NULL) {
318 + *errPos = stoppedAt;
320 + return ParseError(dialogParent, string, stoppedAt, errIn, errMsg);
323 - /* Unroll reversal stack for macros loaded from macros. */
324 - while (NULL != (prog = (Program*) Pop(progStack))) {
325 - RunMacroAsSubrCall(prog);
326 + if (runWindow != NULL) {
328 + if (runWindow->macroCmdData == NULL) {
329 + /* runMacro() is responsable for freeing prog */
330 + runMacro(runWindow, prog);
331 + while (runWindow->macroCmdData != NULL) {
332 + XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
334 + ServerDispatchEvent(&nextEvent);
338 + RunMacroAsSubrCall(prog);
342 + /* we are in 'parse only' mode, therefore release the prog */
346 - /* This stack is empty, so just free it without checking the members. */
347 - XtFree((char*) progStack);
353 @@ -1231,11 +1138,11 @@ void DoMacro(WindowInfo *window, const c
354 strncpy(tMacro, macro, macroLen);
355 tMacro[macroLen] = '\n';
356 tMacro[macroLen+1] = '\0';
358 /* Parse the macro and report errors if it fails */
359 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
360 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
362 ParseError(window->shell, tMacro, stoppedAt, errInName, errMsg);
366 @@ -1495,11 +1402,11 @@ selEnd += $text_length - startLength\n}\
367 sprintf(loopedCmd, loopMacro, command);
369 sprintf(loopedCmd, loopMacro, how, command);
371 /* Parse the resulting macro into an executable program "prog" */
372 - prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt);
373 + prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt, False);
375 fprintf(stderr, "NEdit internal error, repeat macro syntax wrong: %s\n",
379 diff --quilt old/source/nedit.c new/source/nedit.c
380 --- old/source/nedit.c
381 +++ new/source/nedit.c
382 @@ -830,11 +830,11 @@ static int checkDoMacroArg(const char *m
383 strncpy(tMacro, macro, macroLen);
384 tMacro[macroLen] = '\n';
385 tMacro[macroLen+1] = '\0';
387 /* Do a test parse */
388 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
389 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
392 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
395 diff --quilt old/source/parse.h new/source/parse.h
396 --- old/source/parse.h
397 +++ new/source/parse.h
399 #ifndef NEDIT_PARSE_H_INCLUDED
400 #define NEDIT_PARSE_H_INCLUDED
402 #include "interpret.h"
404 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt);
405 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine);
407 #endif /* NEDIT_PARSE_H_INCLUDED */
408 diff --quilt old/source/parse.y new/source/parse.y
409 --- old/source/parse.y
410 +++ new/source/parse.y
411 @@ -73,20 +73,24 @@ static Symbol *matchesActionRoutine(char
414 extern Inst *LoopStack[]; /* addresses of break, cont stmts */
415 extern Inst **LoopStackPtr; /* to fill at the end of a loop */
417 +static int AllowDefine;
425 + AccumulatorData *acc;
427 %token <sym> NUMBER STRING SYMBOL
428 %token DELETE ARG_LOOKUP
429 %token IF WHILE ELSE FOR BREAK CONTINUE RETURN
431 %type <nArgs> arglist
432 %type <inst> cond comastmts for while else and or arrayexpr
436 @@ -109,11 +113,11 @@ extern Inst **LoopStackPtr; /* to fill
442 -program: blank stmts {
443 +program: blank allstmts {
444 ADD_OP(OP_RETURN_NO_VAL); return 0;
446 | blank '{' blank stmts '}' {
447 ADD_OP(OP_RETURN_NO_VAL); return 0;
449 @@ -122,17 +126,63 @@ program: blank stmts {
455 -block: '{' blank stmts '}' blank
456 +blockwb: '{' blank stmts '}' blank
457 | '{' blank '}' blank
472 + /* bail out early, in case of unallowed "define" */
473 + if (!AllowDefine) {
474 + yyerror("macro definitions not allowed"); YYERROR;
478 + $1 = (AccumulatorData *)XtMalloc(sizeof(AccumulatorData));
479 + /* we can't really be sure, that we not overwrite any
482 + ** we should only overwrite installed MACRO_FUNCTION_SYM
483 + ** and this is questionable.
485 + if ($4->type == MACRO_FUNCTION_SYM) {
486 + /* its unsafe to free the old program,
487 + ** maybe it is currently in use
489 + /* FreeProgram($4->value.val.prog) */;
491 + else if ($4->type == LOCAL_SYM ||
492 + $4->type == GLOBAL_SYM) {
493 + /* newly created sym, or we overwrite a local sym */;
495 + yyerror("try to override built-in subroutine"); YYERROR;
497 + $4 = PromoteToGlobal($4);
498 + BeginCreatingProgram($1);
501 + ADD_OP(OP_RETURN_NO_VAL);
502 + Program *prog = FinishCreatingProgram($1);
503 + XtFree((char *)$1);
504 + $4->type = MACRO_FUNCTION_SYM;
505 + $4->value.tag = NO_TAG;
506 + $4->value.val.prog = prog;
509 stmt: simpstmt '\n' blank
510 | IF '(' cond ')' blank block %prec IF_NO_ELSE {
511 SET_BR_OFF($3, GetPC());
513 | IF '(' cond ')' blank block else blank block %prec ELSE {
514 @@ -472,29 +522,35 @@ blank: /* nothing */
515 ** executed using ExecuteProgram. Returns program on success, or NULL
516 ** on failure. If the command failed, the error message is returned
517 ** as a pointer to a static string in msg, and the length of the string up
518 ** to where parsing failed in stoppedAt.
520 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt)
521 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine)
524 + AccumulatorData *acc = (AccumulatorData *)XtMalloc(sizeof(*acc));
526 + BeginCreatingProgram(acc);
528 - BeginCreatingProgram();
529 + /* whether we allow the "define" keyword */
530 + AllowDefine = allowDefine;
532 /* call yyparse to parse the string and check for success. If the parse
533 failed, return the error message and string index (the grammar aborts
534 parsing at the first error) */
539 - FreeProgram(FinishCreatingProgram());
540 + FreeProgram(FinishCreatingProgram(acc));
541 + XtFree((char *)acc);
545 /* get the newly created program */
546 - prog = FinishCreatingProgram();
547 + prog = FinishCreatingProgram(acc);
548 + XtFree((char *)acc);
550 /* parse succeeded */
554 @@ -548,13 +604,12 @@ static int yylex(void)
555 if ((yylval.sym=LookupSymbol(name)) == NULL)
556 yylval.sym = InstallSymbol(name, CONST_SYM, value);
560 - /* process symbol tokens. "define" is a special case not handled
561 - by this parser, considered end of input. Another special case
562 - is action routine names which are allowed to contain '-' despite
563 + /* process symbol tokens. A special case are action
564 + routine names which are allowed to contain '-' despite
565 the ambiguity, handled in matchesActionRoutine. */
566 if (isalpha((unsigned char)*InPtr) || *InPtr == '$') {
567 if ((s=matchesActionRoutine(&InPtr)) == NULL) {
568 char symName[MAX_SYM_LEN+1], *p = symName;
570 @@ -573,14 +628,11 @@ static int yylex(void)
571 if (!strcmp(symName, "continue")) return CONTINUE;
572 if (!strcmp(symName, "return")) return RETURN;
573 if (!strcmp(symName, "in")) return IN;
574 if (!strcmp(symName, "$args")) return ARG_LOOKUP;
575 if (!strcmp(symName, "delete") && follow_non_whitespace('(', SYMBOL, DELETE) == DELETE) return DELETE;
576 - if (!strcmp(symName, "define")) {
580 + if (!strcmp(symName, "define")) return DEFINE;
581 if ((s=LookupSymbol(symName)) == NULL) {
582 s = InstallSymbol(symName, symName[0]=='$' ?
583 (((symName[1] > '0' && symName[1] <= '9') && symName[2] == 0) ?
584 ARG_SYM : GLOBAL_SYM) : LOCAL_SYM, value);
585 s->value.tag = NO_TAG;
586 diff --quilt old/source/smartIndent.c new/source/smartIndent.c
587 --- old/source/smartIndent.c
588 +++ new/source/smartIndent.c
589 @@ -745,21 +745,21 @@ void BeginSmartIndent(WindowInfo *window
590 /* Compile the newline and modify macros and attach them to the window */
591 winData = (windowSmartIndentData *)XtMalloc(sizeof(windowSmartIndentData));
592 winData->inNewLineMacro = 0;
593 winData->inModMacro = 0;
594 winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
596 + &stoppedAt, False);
597 if (winData->newlineMacro == NULL) {
598 ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
599 "newline macro", errMsg);
602 if (indentMacros->modMacro == NULL)
603 winData->modMacro = NULL;
605 winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
607 + &stoppedAt, False);
608 if (winData->modMacro == NULL) {
609 ParseError(window->shell, indentMacros->modMacro, stoppedAt,
610 "smart indent modify macro", errMsg);
613 @@ -1435,11 +1435,11 @@ static int checkSmartIndentDialogData(vo
614 "Newline macro required", "OK");
618 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
619 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
620 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
622 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
623 "newline macro", errMsg);
624 XmTextSetInsertionPosition(SmartIndentDialog.newlineMacro,
625 stoppedAt - widgetText);
626 @@ -1451,11 +1451,11 @@ static int checkSmartIndentDialogData(vo
629 /* Test compile the modify macro */
630 if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
631 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
632 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
633 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
635 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
636 "modify macro", errMsg);
637 XmTextSetInsertionPosition(SmartIndentDialog.modMacro,
638 stoppedAt - widgetText);
639 diff --quilt old/source/userCmds.c new/source/userCmds.c
640 --- old/source/userCmds.c
641 +++ new/source/userCmds.c
642 @@ -2078,11 +2078,11 @@ static int checkMacro(userCmdDialog *ucd
643 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
646 char *errMsg, *stoppedAt;
648 - prog = ParseMacro(macro, &errMsg, &stoppedAt);
649 + prog = ParseMacro(macro, &errMsg, &stoppedAt, False);
651 if (errorParent != NULL) {
652 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
653 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
654 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
655 @@ -3017,11 +3017,11 @@ static char *copyMacroToEnd(char **inPtr
656 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
660 /* Parse the input */
661 - prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
662 + prog = ParseMacro(*inPtr, &errMsg, &stoppedAt, False);
664 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);