drops and reorders
[nedit-bw.git] / parse-define.patch
blobd6c0f5690a6d48adb8672015d4d54c31afaca5de
1 From: Bert Wesarg <bert.wesarg@googlemail.com>
2 Subject: [PATCH v8] parse "define" in the parser
4 This removes the ugly parsing of the "define" keyword. It solves this by
5 saving and restoring the state of the program accumulation. This solves
6 a long standing problam, that parts between "define" definition aren't
7 in the same scope, i.e. this works now:
9 a = 0
11 define t {
14 t_print(a "\n")
16 ChangeLog:
17 v8:
18 * restore behavior in readCheckMacroString. I.e. if the window has a
19 context, don't wait for completion
20 v7:
21 * avoid intra-actions
22 v6:
23 * never assign to a $n variable in yacc
24 v5:
25 * fix memcpy sizeof arguments
26 v4:
27 * collapse run program code into runMacro() call
28 v3:
29 * add missing OP_RETURN_NO_VAL after define
30 v2:
31 * make the define statement special, prevents recursion of the define
32 keyword
33 * need to PromoteToGlobal() the new function symbol
34 * prevent overriding of non macro functions
36 ---
38 source/interpret.c | 24 ++++++--
39 source/interpret.h | 16 +++++
40 source/macro.c | 140 ++++++++-------------------------------------------
41 source/nedit.c | 2
42 source/parse.h | 2
43 source/parse.y | 83 +++++++++++++++++++++++++-----
44 source/smartIndent.c | 8 +-
45 source/userCmds.c | 4 -
46 8 files changed, 131 insertions(+), 148 deletions(-)
48 diff --quilt old/source/interpret.c new/source/interpret.c
49 --- old/source/interpret.c
50 +++ new/source/interpret.c
51 @@ -62,10 +62,7 @@ static const char CVSID[] = "$Id: interp
52 #include "../debug.h"
53 #endif
55 -#define PROGRAM_SIZE 4096 /* Maximum program size */
56 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
57 -#define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
58 - allowed per program */
59 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
60 allowed to execute before preempting and
61 returning to allow other things to run */
62 @@ -238,8 +235,15 @@ void InitMacroGlobals(void)
63 ** Start collecting instructions for a program. Clears the program
64 ** and the symbol table.
66 -void BeginCreatingProgram(void)
67 -{
68 +void BeginCreatingProgram(AccumulatorData *acc)
70 + /* save state */
71 + acc->localSymList = LocalSymList;
72 + memcpy(acc->prog, Prog, sizeof(*Prog) * PROGRAM_SIZE);
73 + acc->progP = ProgP;
74 + memcpy(acc->loopStack, LoopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
75 + acc->loopStackPtr = LoopStackPtr;
77 LocalSymList = NULL;
78 ProgP = Prog;
79 LoopStackPtr = LoopStack;
80 @@ -250,7 +254,7 @@ void BeginCreatingProgram(void)
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)
87 Program *newProg;
88 int progLen, fpOffset = 0;
89 @@ -262,7 +266,6 @@ Program *FinishCreatingProgram(void)
90 memcpy(newProg->code, Prog, progLen);
91 newProg->localSymList = LocalSymList;
92 newProg->refcount = 1;
93 - LocalSymList = NULL;
95 /* Local variables' values are stored on the stack. Here we assign
96 frame pointer offsets to them. */
97 @@ -271,6 +274,13 @@ Program *FinishCreatingProgram(void)
99 DISASM(newProg->code, ProgP - Prog);
101 + /* restore state */
102 + LocalSymList = acc->localSymList;
103 + memcpy(Prog, acc->prog, sizeof(*Prog) * PROGRAM_SIZE);
104 + ProgP = acc->progP;
105 + memcpy(LoopStack, acc->loopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
106 + LoopStackPtr = acc->loopStackPtr;
108 return newProg;
111 diff --quilt old/source/interpret.h new/source/interpret.h
112 --- old/source/interpret.h
113 +++ new/source/interpret.h
114 @@ -36,6 +36,9 @@
115 #define MACRO_EVENT_MARKER 2 /* Special value for the send_event field of
116 events passed to action routines. Tells
117 them that they were called from a macro */
118 +#define PROGRAM_SIZE 4096 /* Maximum program size */
119 +#define LOOP_STACK_SIZE 256 /* (Approx.) Number of break/continue stmts
120 + allowed per program */
122 enum symTypes {CONST_SYM, GLOBAL_SYM, LOCAL_SYM, ARG_SYM, PROC_VALUE_SYM,
123 C_FUNCTION_SYM, MACRO_FUNCTION_SYM, ACTION_ROUTINE_SYM};
124 @@ -116,6 +119,15 @@ typedef struct {
125 WindowInfo *focusWindow;
126 } RestartData;
128 +/* state of the accumulator (aka compiler) */
129 +typedef struct AccumulatorDataTag {
130 + Symbol *localSymList;
131 + Inst prog[PROGRAM_SIZE];
132 + Inst *progP;
133 + Inst *loopStack[LOOP_STACK_SIZE];
134 + Inst **loopStackPtr;
135 +} AccumulatorData;
137 void InitMacroGlobals(void);
139 SparseArrayEntry *arrayIterateFirst(DataValue *theArray);
140 @@ -130,7 +142,7 @@ int ArrayCopy(DataValue *dstArray, DataV
142 /* Routines for creating a program, (accumulated beginning with
143 BeginCreatingProgram and returned via FinishCreatingProgram) */
144 -void BeginCreatingProgram(void);
145 +void BeginCreatingProgram(AccumulatorData *acc);
146 int AddOp(int op, char **msg);
147 int AddSym(Symbol *sym, char **msg);
148 int AddImmediate(int value, char **msg);
149 @@ -141,7 +153,7 @@ Symbol *LookupStringConstSymbol(const ch
150 Symbol *InstallStringConstSymbol(const char *str);
151 Symbol *LookupSymbol(const char *name);
152 Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value);
153 -Program *FinishCreatingProgram(void);
154 +Program *FinishCreatingProgram(AccumulatorData *acc);
155 void SwapCode(Inst *start, Inst *boundary, Inst *end);
156 void StartLoopAddrList(void);
157 int AddBreakAddr(Inst *addr);
158 diff --quilt old/source/macro.c new/source/macro.c
159 --- old/source/macro.c
160 +++ new/source/macro.c
161 @@ -756,7 +756,7 @@ void Replay(WindowInfo *window)
162 window->macroCmdData == NULL) {
163 /* Parse the replay macro (it's stored in text form) and compile it into
164 an executable program "prog" */
165 - prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt);
166 + prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt, False);
167 if (prog == NULL) {
168 fprintf(stderr,
169 "NEdit internal error, learn/replay macro syntax error: %s\n",
170 @@ -851,126 +851,32 @@ int CheckMacroString(Widget dialogParent
171 static int readCheckMacroString(Widget dialogParent, char *string,
172 WindowInfo *runWindow, const char *errIn, char **errPos)
174 - char *stoppedAt, *inPtr, *namePtr, *errMsg;
175 - char subrName[MAX_SYM_LEN];
176 + char *stoppedAt, *errMsg;
177 Program *prog;
178 - Symbol *sym;
179 - DataValue subrPtr;
180 - Stack* progStack = (Stack*) XtMalloc(sizeof(Stack));
181 - progStack->top = NULL;
182 - progStack->size = 0;
184 - inPtr = string;
185 - while (*inPtr != '\0') {
187 - /* skip over white space and comments */
188 - while (*inPtr==' ' || *inPtr=='\t' || *inPtr=='\n'|| *inPtr=='#') {
189 - if (*inPtr == '#')
190 - while (*inPtr != '\n' && *inPtr != '\0') inPtr++;
191 - else
192 - inPtr++;
194 - if (*inPtr == '\0')
195 - break;
197 - /* look for define keyword, and compile and store defined routines */
198 - if (!strncmp(inPtr, "define", 6) && (inPtr[6]==' ' || inPtr[6]=='\t')) {
199 - inPtr += 6;
200 - inPtr += strspn(inPtr, " \t\n");
201 - namePtr = subrName;
202 - while ((namePtr < &subrName[MAX_SYM_LEN - 1])
203 - && (isalnum((unsigned char)*inPtr) || *inPtr == '_')) {
204 - *namePtr++ = *inPtr++;
206 - *namePtr = '\0';
207 - if (isalnum((unsigned char)*inPtr) || *inPtr == '_') {
208 - return ParseError(dialogParent, string, inPtr, errIn,
209 - "subroutine name too long");
211 - inPtr += strspn(inPtr, " \t\n");
212 - if (*inPtr != '{') {
213 - if (errPos != NULL) *errPos = stoppedAt;
214 - return ParseError(dialogParent, string, inPtr,
215 - errIn, "expected '{'");
217 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
218 - if (prog == NULL) {
219 - if (errPos != NULL) *errPos = stoppedAt;
220 - return ParseError(dialogParent, string, stoppedAt,
221 - errIn, errMsg);
223 - if (runWindow != NULL) {
224 - sym = LookupSymbol(subrName);
225 - if (sym == NULL) {
226 - subrPtr.val.prog = prog;
227 - subrPtr.tag = NO_TAG;
228 - sym = InstallSymbol(subrName, MACRO_FUNCTION_SYM, subrPtr);
229 - } else {
230 - if (sym->type == MACRO_FUNCTION_SYM)
231 - FreeProgram(sym->value.val.prog);
232 - else
233 - sym->type = MACRO_FUNCTION_SYM;
234 - sym->value.val.prog = prog;
237 - inPtr = stoppedAt;
239 - /* Parse and execute immediate (outside of any define) macro commands
240 - and WAIT for them to finish executing before proceeding. Note that
241 - the code below is not perfect. If you interleave code blocks with
242 - definitions in a file which is loaded from another macro file, it
243 - will probably run the code blocks in reverse order! */
244 - } else {
245 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
246 - if (prog == NULL) {
247 - if (errPos != NULL) {
248 - *errPos = stoppedAt;
251 - return ParseError(dialogParent, string, stoppedAt,
252 - errIn, errMsg);
255 - if (runWindow != NULL) {
256 - XEvent nextEvent;
257 - if (runWindow->macroCmdData == NULL) {
258 - /* runMacro() is responsable for freeing prog */
259 - runMacro(runWindow, prog);
260 - while (runWindow->macroCmdData != NULL) {
261 - XtAppNextEvent(XtWidgetToApplicationContext(
262 - runWindow->shell), &nextEvent);
263 - ServerDispatchEvent(&nextEvent);
265 - } else {
266 - /* If we come here this means that the string was parsed
267 - from within another macro via load_macro_file(). In
268 - this case, plain code segments outside of define
269 - blocks are rolled into one Program each and put on
270 - the stack. At the end, the stack is unrolled, so the
271 - plain Programs would be executed in the wrong order.
273 - So we don't hand the Programs over to the interpreter
274 - just yet (via RunMacroAsSubrCall()), but put it on a
275 - stack of our own, reversing order once again. */
276 - Push(progStack, (void*) prog);
279 - inPtr = stoppedAt;
282 - /* we are in 'parse only' mode, therefore release the prog */
283 - if (runWindow == NULL) {
284 - FreeProgram(prog);
285 + prog = ParseMacro(string, &errMsg, &stoppedAt, True);
286 + if (prog == NULL) {
287 + if (errPos != NULL) {
288 + *errPos = stoppedAt;
290 + return ParseError(dialogParent, string, stoppedAt, errIn, errMsg);
293 - /* Unroll reversal stack for macros loaded from macros. */
294 - while (NULL != (prog = (Program*) Pop(progStack))) {
295 - RunMacroAsSubrCall(prog);
296 + if (runWindow != NULL) {
297 + int waitForCompletion = runWindow->macroCmdData == NULL;
298 + XEvent nextEvent;
299 + /* runMacro() is responsable for freeing prog */
300 + runMacro(runWindow, prog);
301 + while (waitForCompletion && runWindow->macroCmdData != NULL) {
302 + XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
303 + &nextEvent);
304 + ServerDispatchEvent(&nextEvent);
307 + else {
308 + /* we are in 'parse only' mode, therefore release the prog */
309 + FreeProgram(prog);
312 - /* This stack is empty, so just free it without checking the members. */
313 - XtFree((char*) progStack);
315 return True;
317 @@ -1230,7 +1136,7 @@ void DoMacro(WindowInfo *window, const c
318 tMacro[macroLen+1] = '\0';
320 /* Parse the macro and report errors if it fails */
321 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
322 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
323 if (prog == NULL) {
324 ParseError(window->shell, tMacro, stoppedAt, errInName, errMsg);
325 XtFree(tMacro);
326 @@ -1494,7 +1400,7 @@ selEnd += $text_length - startLength\n}\
327 sprintf(loopedCmd, loopMacro, how, command);
329 /* Parse the resulting macro into an executable program "prog" */
330 - prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt);
331 + prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt, False);
332 if (prog == NULL) {
333 fprintf(stderr, "NEdit internal error, repeat macro syntax wrong: %s\n",
334 errMsg);
335 diff --quilt old/source/nedit.c new/source/nedit.c
336 --- old/source/nedit.c
337 +++ new/source/nedit.c
338 @@ -832,7 +832,7 @@ static int checkDoMacroArg(const char *m
339 tMacro[macroLen+1] = '\0';
341 /* Do a test parse */
342 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
343 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
344 XtFree(tMacro);
345 if (prog == NULL) {
346 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
347 diff --quilt old/source/parse.h new/source/parse.h
348 --- old/source/parse.h
349 +++ new/source/parse.h
350 @@ -30,6 +30,6 @@
352 #include "interpret.h"
354 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt);
355 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine);
357 #endif /* NEDIT_PARSE_H_INCLUDED */
358 diff --quilt old/source/parse.y new/source/parse.y
359 --- old/source/parse.y
360 +++ new/source/parse.y
361 @@ -75,19 +75,26 @@ static char *InPtr;
362 extern Inst *LoopStack[]; /* addresses of break, cont stmts */
363 extern Inst **LoopStackPtr; /* to fill at the end of a loop */
365 +static int AllowDefine;
369 %union {
370 Symbol *sym;
371 Inst *inst;
372 int nArgs;
373 + struct {
374 + AccumulatorData *acc;
375 + Symbol *sym;
376 + } define;
378 %token <sym> NUMBER STRING SYMBOL
379 %token DELETE ARG_LOOKUP
380 -%token IF WHILE ELSE FOR BREAK CONTINUE RETURN
381 +%token IF WHILE ELSE FOR BREAK CONTINUE RETURN DEFINE
382 %type <nArgs> arglist
383 %type <inst> cond comastmts for while else and or arrayexpr
384 %type <sym> evalsym
385 +%type <define> definesym
387 %nonassoc IF_NO_ELSE
388 %nonassoc ELSE
389 @@ -111,7 +118,7 @@ extern Inst **LoopStackPtr; /* to fill
391 %% /* Rules */
393 -program: blank stmts {
394 +program: blank topstmts {
395 ADD_OP(OP_RETURN_NO_VAL); return 0;
397 | blank '{' blank stmts '}' {
398 @@ -124,13 +131,60 @@ program: blank stmts {
399 return 1;
402 -block: '{' blank stmts '}' blank
403 +blockwb: '{' blank stmts '}' blank
404 | '{' blank '}' blank
406 +block: blockwb
407 | stmt
409 stmts: stmt
410 | stmts stmt
412 +topstmts: topstmt
413 + | topstmts topstmt
415 +topstmt: stmt
416 + | define
419 +definekw: DEFINE {
420 + /* bail out early, in case of unallowed "define" */
421 + if (!AllowDefine) {
422 + yyerror("macro definitions not allowed"); YYERROR;
426 +definesym: SYMBOL {
427 + $$.acc = XtNew(AccumulatorData);
428 + /* we can't really be sure, that we not overwrite any
429 + ** wrong symbol
430 + **
431 + ** we should only overwrite installed MACRO_FUNCTION_SYM
432 + ** and this is questionable.
433 + */
434 + if ($1->type == MACRO_FUNCTION_SYM) {
435 + FreeProgram($1->value.val.prog);
437 + else if ($1->type == LOCAL_SYM ||
438 + $1->type == GLOBAL_SYM) {
439 + /* newly created sym, or we overwrite a local sym */;
440 + } else {
441 + yyerror("try to override built-in subroutine"); YYERROR;
443 + $$.sym = PromoteToGlobal($1);
444 + BeginCreatingProgram($$.acc);
447 +define: definekw blank definesym blank blockwb {
448 + ADD_OP(OP_RETURN_NO_VAL);
449 + Program *prog = FinishCreatingProgram($3.acc);
450 + XtFree((char *)$3.acc);
451 + $3.sym->type = MACRO_FUNCTION_SYM;
452 + $3.sym->value.tag = NO_TAG;
453 + $3.sym->value.val.prog = prog;
457 stmt: simpstmt '\n' blank
458 | IF '(' cond ')' blank block %prec IF_NO_ELSE {
459 SET_BR_OFF($3, GetPC());
460 @@ -474,17 +528,20 @@ blank: /* nothing */
461 ** as a pointer to a static string in msg, and the length of the string up
462 ** to where parsing failed in stoppedAt.
464 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt)
465 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine)
467 Program *prog;
468 + AccumulatorData *acc = XtNew(AccumulatorData);
470 #if YYDEBUG
471 int oldyydebug = yydebug;
472 yydebug = 1;
473 #endif
475 + BeginCreatingProgram(acc);
477 - BeginCreatingProgram();
478 + /* whether we allow the "define" keyword */
479 + AllowDefine = allowDefine;
481 /* call yyparse to parse the string and check for success. If the parse
482 failed, return the error message and string index (the grammar aborts
483 @@ -493,7 +550,8 @@ Program *ParseMacro(char *expr, char **m
484 if (yyparse()) {
485 *msg = ErrMsg;
486 *stoppedAt = InPtr;
487 - FreeProgram(FinishCreatingProgram());
488 + FreeProgram(FinishCreatingProgram(acc));
489 + XtFree((char *)acc);
491 #if YYDEBUG
492 yydebug = oldyydebug;
493 @@ -503,7 +561,8 @@ Program *ParseMacro(char *expr, char **m
496 /* get the newly created program */
497 - prog = FinishCreatingProgram();
498 + prog = FinishCreatingProgram(acc);
499 + XtFree((char *)acc);
501 /* parse succeeded */
502 *msg = "";
503 @@ -566,9 +625,8 @@ static int yylex(void)
504 return NUMBER;
507 - /* process symbol tokens. "define" is a special case not handled
508 - by this parser, considered end of input. Another special case
509 - is action routine names which are allowed to contain '-' despite
510 + /* process symbol tokens. A special case are action
511 + routine names which are allowed to contain '-' despite
512 the ambiguity, handled in matchesActionRoutine. */
513 if (isalpha((unsigned char)*InPtr) || *InPtr == '$') {
514 if ((s=matchesActionRoutine(&InPtr)) == NULL) {
515 @@ -591,10 +649,7 @@ static int yylex(void)
516 if (!strcmp(symName, "in")) return IN;
517 if (!strcmp(symName, "$args")) return ARG_LOOKUP;
518 if (!strcmp(symName, "delete") && follow_non_whitespace('(', SYMBOL, DELETE) == DELETE) return DELETE;
519 - if (!strcmp(symName, "define")) {
520 - InPtr -= 6;
521 - return 0;
523 + if (!strcmp(symName, "define")) return DEFINE;
524 if ((s=LookupSymbol(symName)) == NULL) {
525 s = InstallSymbol(symName, symName[0]=='$' ?
526 (((symName[1] > '0' && symName[1] <= '9') && symName[2] == 0) ?
527 diff --quilt old/source/smartIndent.c new/source/smartIndent.c
528 --- old/source/smartIndent.c
529 +++ new/source/smartIndent.c
530 @@ -747,7 +747,7 @@ void BeginSmartIndent(WindowInfo *window
531 winData->inNewLineMacro = 0;
532 winData->inModMacro = 0;
533 winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
534 - &stoppedAt);
535 + &stoppedAt, False);
536 if (winData->newlineMacro == NULL) {
537 XtFree((char *)winData);
538 ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
539 @@ -758,7 +758,7 @@ void BeginSmartIndent(WindowInfo *window
540 winData->modMacro = NULL;
541 else {
542 winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
543 - &stoppedAt);
544 + &stoppedAt, False);
545 if (winData->modMacro == NULL) {
546 FreeProgram(winData->newlineMacro);
547 XtFree((char *)winData);
548 @@ -1440,7 +1440,7 @@ static int checkSmartIndentDialogData(vo
551 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
552 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
553 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
554 if (prog == NULL) {
555 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
556 "newline macro", errMsg);
557 @@ -1456,7 +1456,7 @@ static int checkSmartIndentDialogData(vo
558 /* Test compile the modify macro */
559 if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
560 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
561 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
562 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
563 if (prog == NULL) {
564 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
565 "modify macro", errMsg);
566 diff --quilt old/source/userCmds.c new/source/userCmds.c
567 --- old/source/userCmds.c
568 +++ new/source/userCmds.c
569 @@ -2080,7 +2080,7 @@ static int checkMacroText(char *macro, W
570 Program *prog;
571 char *errMsg, *stoppedAt;
573 - prog = ParseMacro(macro, &errMsg, &stoppedAt);
574 + prog = ParseMacro(macro, &errMsg, &stoppedAt, False);
575 if (prog == NULL) {
576 if (errorParent != NULL) {
577 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
578 @@ -3019,7 +3019,7 @@ static char *copyMacroToEnd(char **inPtr
581 /* Parse the input */
582 - prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
583 + prog = ParseMacro(*inPtr, &errMsg, &stoppedAt, False);
584 if (prog == NULL) {
585 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
586 return NULL;