final step to context based macro interpreter
[nedit-bw.git] / parse-define.patch
blobe9b2aa5290a4d40889eaf6ccd19c5997d8962aba
1 From: Bert Wesarg <bert.wesarg@googlemail.com>
2 Subject: [PATCH v4] 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 v4:
18 * collapse run program code into runMacro() call
19 v3:
20 * add missing OP_RETURN_NO_VAL after define
21 v2:
22 * make the define statement special, prevents recursion of the define
23 keyword
24 * need to PromoteToGlobal() the new function symbol
25 * prevent overriding of non macro functions
27 ---
29 source/interpret.c | 24 ++++++--
30 source/interpret.h | 16 +++++
31 source/macro.c | 139 ++++++++-------------------------------------------
32 source/nedit.c | 2
33 source/parse.h | 2
34 source/parse.y | 75 ++++++++++++++++++++++-----
35 source/smartIndent.c | 8 +-
36 source/userCmds.c | 4 -
37 8 files changed, 123 insertions(+), 147 deletions(-)
39 diff --quilt old/source/interpret.c new/source/interpret.c
40 --- old/source/interpret.c
41 +++ new/source/interpret.c
42 @@ -62,10 +62,7 @@ static const char CVSID[] = "$Id: interp
43 #include "../debug.h"
44 #endif
46 -#define PROGRAM_SIZE 4096 /* Maximum program size */
47 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
48 -#define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
49 - allowed per program */
50 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
51 allowed to execute before preempting and
52 returning to allow other things to run */
53 @@ -238,8 +235,15 @@ void InitMacroGlobals(void)
54 ** Start collecting instructions for a program. Clears the program
55 ** and the symbol table.
57 -void BeginCreatingProgram(void)
58 -{
59 +void BeginCreatingProgram(AccumulatorData *acc)
61 + /* save state */
62 + acc->localSymList = LocalSymList;
63 + memcpy(acc->prog, Prog, sizeof(*Prog) * PROGRAM_SIZE);
64 + acc->progP = ProgP;
65 + memcpy(acc->loopStack, LoopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
66 + acc->loopStackPtr = LoopStackPtr;
68 LocalSymList = NULL;
69 ProgP = Prog;
70 LoopStackPtr = LoopStack;
71 @@ -250,7 +254,7 @@ void BeginCreatingProgram(void)
72 ** symbol table) as a package that ExecuteMacro can execute. This
73 ** program must be freed with FreeProgram.
75 -Program *FinishCreatingProgram(void)
76 +Program *FinishCreatingProgram(AccumulatorData *acc)
78 Program *newProg;
79 int progLen, fpOffset = 0;
80 @@ -262,7 +266,6 @@ Program *FinishCreatingProgram(void)
81 memcpy(newProg->code, Prog, progLen);
82 newProg->localSymList = LocalSymList;
83 newProg->refcount = 1;
84 - LocalSymList = NULL;
86 /* Local variables' values are stored on the stack. Here we assign
87 frame pointer offsets to them. */
88 @@ -271,6 +274,13 @@ Program *FinishCreatingProgram(void)
90 DISASM(newProg->code, ProgP - Prog);
92 + /* restore state */
93 + LocalSymList = acc->localSymList;
94 + memcpy(Prog, acc->prog, sizeof(*Prog) * PROGRAM_SIZE);
95 + ProgP = acc->progP;
96 + memcpy(LoopStack, acc->loopStack, sizeof(*LoopStack) * LOOP_STACK_SIZE);
97 + LoopStackPtr = acc->loopStackPtr;
99 return newProg;
102 diff --quilt old/source/interpret.h new/source/interpret.h
103 --- old/source/interpret.h
104 +++ new/source/interpret.h
105 @@ -36,6 +36,9 @@
106 #define MACRO_EVENT_MARKER 2 /* Special value for the send_event field of
107 events passed to action routines. Tells
108 them that they were called from a macro */
109 +#define PROGRAM_SIZE 4096 /* Maximum program size */
110 +#define LOOP_STACK_SIZE 256 /* (Approx.) Number of break/continue stmts
111 + allowed per program */
113 enum symTypes {CONST_SYM, GLOBAL_SYM, LOCAL_SYM, ARG_SYM, PROC_VALUE_SYM,
114 C_FUNCTION_SYM, MACRO_FUNCTION_SYM, ACTION_ROUTINE_SYM};
115 @@ -116,6 +119,15 @@ typedef struct {
116 WindowInfo *focusWindow;
117 } RestartData;
119 +/* state of the accumulator (aka compiler) */
120 +typedef struct AccumulatorDataTag {
121 + Symbol *localSymList;
122 + Inst prog[PROGRAM_SIZE];
123 + Inst *progP;
124 + Inst *loopStack[LOOP_STACK_SIZE];
125 + Inst **loopStackPtr;
126 +} AccumulatorData;
128 void InitMacroGlobals(void);
130 SparseArrayEntry *arrayIterateFirst(DataValue *theArray);
131 @@ -130,7 +142,7 @@ int ArrayCopy(DataValue *dstArray, DataV
133 /* Routines for creating a program, (accumulated beginning with
134 BeginCreatingProgram and returned via FinishCreatingProgram) */
135 -void BeginCreatingProgram(void);
136 +void BeginCreatingProgram(AccumulatorData *acc);
137 int AddOp(int op, char **msg);
138 int AddSym(Symbol *sym, char **msg);
139 int AddImmediate(int value, char **msg);
140 @@ -141,7 +153,7 @@ Symbol *LookupStringConstSymbol(const ch
141 Symbol *InstallStringConstSymbol(const char *str);
142 Symbol *LookupSymbol(const char *name);
143 Symbol *InstallSymbol(const char *name, enum symTypes type, DataValue value);
144 -Program *FinishCreatingProgram(void);
145 +Program *FinishCreatingProgram(AccumulatorData *acc);
146 void SwapCode(Inst *start, Inst *boundary, Inst *end);
147 void StartLoopAddrList(void);
148 int AddBreakAddr(Inst *addr);
149 diff --quilt old/source/macro.c new/source/macro.c
150 --- old/source/macro.c
151 +++ new/source/macro.c
152 @@ -756,7 +756,7 @@ void Replay(WindowInfo *window)
153 window->macroCmdData == NULL) {
154 /* Parse the replay macro (it's stored in text form) and compile it into
155 an executable program "prog" */
156 - prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt);
157 + prog = ParseMacro(ReplayMacro, &errMsg, &stoppedAt, False);
158 if (prog == NULL) {
159 fprintf(stderr,
160 "NEdit internal error, learn/replay macro syntax error: %s\n",
161 @@ -851,126 +851,31 @@ int CheckMacroString(Widget dialogParent
162 static int readCheckMacroString(Widget dialogParent, char *string,
163 WindowInfo *runWindow, const char *errIn, char **errPos)
165 - char *stoppedAt, *inPtr, *namePtr, *errMsg;
166 - char subrName[MAX_SYM_LEN];
167 + char *stoppedAt, *errMsg;
168 Program *prog;
169 - Symbol *sym;
170 - DataValue subrPtr;
171 - Stack* progStack = (Stack*) XtMalloc(sizeof(Stack));
172 - progStack->top = NULL;
173 - progStack->size = 0;
175 - inPtr = string;
176 - while (*inPtr != '\0') {
178 - /* skip over white space and comments */
179 - while (*inPtr==' ' || *inPtr=='\t' || *inPtr=='\n'|| *inPtr=='#') {
180 - if (*inPtr == '#')
181 - while (*inPtr != '\n' && *inPtr != '\0') inPtr++;
182 - else
183 - inPtr++;
185 - if (*inPtr == '\0')
186 - break;
188 - /* look for define keyword, and compile and store defined routines */
189 - if (!strncmp(inPtr, "define", 6) && (inPtr[6]==' ' || inPtr[6]=='\t')) {
190 - inPtr += 6;
191 - inPtr += strspn(inPtr, " \t\n");
192 - namePtr = subrName;
193 - while ((namePtr < &subrName[MAX_SYM_LEN - 1])
194 - && (isalnum((unsigned char)*inPtr) || *inPtr == '_')) {
195 - *namePtr++ = *inPtr++;
197 - *namePtr = '\0';
198 - if (isalnum((unsigned char)*inPtr) || *inPtr == '_') {
199 - return ParseError(dialogParent, string, inPtr, errIn,
200 - "subroutine name too long");
202 - inPtr += strspn(inPtr, " \t\n");
203 - if (*inPtr != '{') {
204 - if (errPos != NULL) *errPos = stoppedAt;
205 - return ParseError(dialogParent, string, inPtr,
206 - errIn, "expected '{'");
208 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
209 - if (prog == NULL) {
210 - if (errPos != NULL) *errPos = stoppedAt;
211 - return ParseError(dialogParent, string, stoppedAt,
212 - errIn, errMsg);
214 - if (runWindow != NULL) {
215 - sym = LookupSymbol(subrName);
216 - if (sym == NULL) {
217 - subrPtr.val.prog = prog;
218 - subrPtr.tag = NO_TAG;
219 - sym = InstallSymbol(subrName, MACRO_FUNCTION_SYM, subrPtr);
220 - } else {
221 - if (sym->type == MACRO_FUNCTION_SYM)
222 - FreeProgram(sym->value.val.prog);
223 - else
224 - sym->type = MACRO_FUNCTION_SYM;
225 - sym->value.val.prog = prog;
228 - inPtr = stoppedAt;
230 - /* Parse and execute immediate (outside of any define) macro commands
231 - and WAIT for them to finish executing before proceeding. Note that
232 - the code below is not perfect. If you interleave code blocks with
233 - definitions in a file which is loaded from another macro file, it
234 - will probably run the code blocks in reverse order! */
235 - } else {
236 - prog = ParseMacro(inPtr, &errMsg, &stoppedAt);
237 - if (prog == NULL) {
238 - if (errPos != NULL) {
239 - *errPos = stoppedAt;
242 - return ParseError(dialogParent, string, stoppedAt,
243 - errIn, errMsg);
246 - if (runWindow != NULL) {
247 - XEvent nextEvent;
248 - if (runWindow->macroCmdData == NULL) {
249 - /* runMacro() is responsable for freeing prog */
250 - runMacro(runWindow, prog);
251 - while (runWindow->macroCmdData != NULL) {
252 - XtAppNextEvent(XtWidgetToApplicationContext(
253 - runWindow->shell), &nextEvent);
254 - ServerDispatchEvent(&nextEvent);
256 - } else {
257 - /* If we come here this means that the string was parsed
258 - from within another macro via load_macro_file(). In
259 - this case, plain code segments outside of define
260 - blocks are rolled into one Program each and put on
261 - the stack. At the end, the stack is unrolled, so the
262 - plain Programs would be executed in the wrong order.
264 - So we don't hand the Programs over to the interpreter
265 - just yet (via RunMacroAsSubrCall()), but put it on a
266 - stack of our own, reversing order once again. */
267 - Push(progStack, (void*) prog);
270 - inPtr = stoppedAt;
273 - /* we are in 'parse only' mode, therefore release the prog */
274 - if (runWindow == NULL) {
275 - FreeProgram(prog);
276 + prog = ParseMacro(string, &errMsg, &stoppedAt, True);
277 + if (prog == NULL) {
278 + if (errPos != NULL) {
279 + *errPos = stoppedAt;
281 + return ParseError(dialogParent, string, stoppedAt, errIn, errMsg);
284 - /* Unroll reversal stack for macros loaded from macros. */
285 - while (NULL != (prog = (Program*) Pop(progStack))) {
286 - RunMacroAsSubrCall(prog);
287 + if (runWindow != NULL) {
288 + XEvent nextEvent;
289 + /* runMacro() is responsable for freeing prog */
290 + runMacro(runWindow, prog);
291 + while (runWindow->macroCmdData != NULL) {
292 + XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
293 + &nextEvent);
294 + ServerDispatchEvent(&nextEvent);
297 + else {
298 + /* we are in 'parse only' mode, therefore release the prog */
299 + FreeProgram(prog);
302 - /* This stack is empty, so just free it without checking the members. */
303 - XtFree((char*) progStack);
305 return True;
307 @@ -1230,7 +1135,7 @@ void DoMacro(WindowInfo *window, const c
308 tMacro[macroLen+1] = '\0';
310 /* Parse the macro and report errors if it fails */
311 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
312 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
313 if (prog == NULL) {
314 ParseError(window->shell, tMacro, stoppedAt, errInName, errMsg);
315 XtFree(tMacro);
316 @@ -1494,7 +1399,7 @@ selEnd += $text_length - startLength\n}\
317 sprintf(loopedCmd, loopMacro, how, command);
319 /* Parse the resulting macro into an executable program "prog" */
320 - prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt);
321 + prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt, False);
322 if (prog == NULL) {
323 fprintf(stderr, "NEdit internal error, repeat macro syntax wrong: %s\n",
324 errMsg);
325 diff --quilt old/source/nedit.c new/source/nedit.c
326 --- old/source/nedit.c
327 +++ new/source/nedit.c
328 @@ -832,7 +832,7 @@ static int checkDoMacroArg(const char *m
329 tMacro[macroLen+1] = '\0';
331 /* Do a test parse */
332 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
333 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
334 XtFree(tMacro);
335 if (prog == NULL) {
336 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
337 diff --quilt old/source/parse.h new/source/parse.h
338 --- old/source/parse.h
339 +++ new/source/parse.h
340 @@ -30,6 +30,6 @@
342 #include "interpret.h"
344 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt);
345 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine);
347 #endif /* NEDIT_PARSE_H_INCLUDED */
348 diff --quilt old/source/parse.y new/source/parse.y
349 --- old/source/parse.y
350 +++ new/source/parse.y
351 @@ -75,16 +75,20 @@ static char *InPtr;
352 extern Inst *LoopStack[]; /* addresses of break, cont stmts */
353 extern Inst **LoopStackPtr; /* to fill at the end of a loop */
355 +static int AllowDefine;
359 %union {
360 Symbol *sym;
361 Inst *inst;
362 int nArgs;
363 + AccumulatorData *acc;
365 %token <sym> NUMBER STRING SYMBOL
366 %token DELETE ARG_LOOKUP
367 %token IF WHILE ELSE FOR BREAK CONTINUE RETURN
368 +%token <acc> DEFINE
369 %type <nArgs> arglist
370 %type <inst> cond comastmts for while else and or arrayexpr
371 %type <sym> evalsym
372 @@ -111,7 +115,7 @@ extern Inst **LoopStackPtr; /* to fill
374 %% /* Rules */
376 -program: blank stmts {
377 +program: blank allstmts {
378 ADD_OP(OP_RETURN_NO_VAL); return 0;
380 | blank '{' blank stmts '}' {
381 @@ -124,13 +128,56 @@ program: blank stmts {
382 return 1;
385 -block: '{' blank stmts '}' blank
386 +blockwb: '{' blank stmts '}' blank
387 | '{' blank '}' blank
389 +block: blockwb
390 | stmt
392 stmts: stmt
393 | stmts stmt
395 +allstmts: allstmt
396 + | allstmts allstmt
398 +allstmt: stmt
399 + | define
401 +define: DEFINE {
402 + /* bail out early, in case of unallowed "define" */
403 + if (!AllowDefine) {
404 + yyerror("macro definitions not allowed"); YYERROR;
407 + blank SYMBOL {
408 + $1 = (AccumulatorData *)XtMalloc(sizeof(AccumulatorData));
409 + /* we can't really be sure, that we not overwrite any
410 + ** wrong symbol
411 + **
412 + ** we should only overwrite installed MACRO_FUNCTION_SYM
413 + ** and this is questionable.
414 + */
415 + if ($4->type == MACRO_FUNCTION_SYM) {
416 + FreeProgram($4->value.val.prog);
418 + else if ($4->type == LOCAL_SYM ||
419 + $4->type == GLOBAL_SYM) {
420 + /* newly created sym, or we overwrite a local sym */;
421 + } else {
422 + yyerror("try to override built-in subroutine"); YYERROR;
424 + $4 = PromoteToGlobal($4);
425 + BeginCreatingProgram($1);
427 + blank blockwb {
428 + ADD_OP(OP_RETURN_NO_VAL);
429 + Program *prog = FinishCreatingProgram($1);
430 + XtFree((char *)$1);
431 + $4->type = MACRO_FUNCTION_SYM;
432 + $4->value.tag = NO_TAG;
433 + $4->value.val.prog = prog;
436 stmt: simpstmt '\n' blank
437 | IF '(' cond ')' blank block %prec IF_NO_ELSE {
438 SET_BR_OFF($3, GetPC());
439 @@ -474,11 +521,15 @@ blank: /* nothing */
440 ** as a pointer to a static string in msg, and the length of the string up
441 ** to where parsing failed in stoppedAt.
443 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt)
444 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine)
446 Program *prog;
447 + AccumulatorData *acc = (AccumulatorData *)XtMalloc(sizeof(*acc));
449 + BeginCreatingProgram(acc);
451 - BeginCreatingProgram();
452 + /* whether we allow the "define" keyword */
453 + AllowDefine = allowDefine;
455 /* call yyparse to parse the string and check for success. If the parse
456 failed, return the error message and string index (the grammar aborts
457 @@ -487,12 +538,14 @@ Program *ParseMacro(char *expr, char **m
458 if (yyparse()) {
459 *msg = ErrMsg;
460 *stoppedAt = InPtr;
461 - FreeProgram(FinishCreatingProgram());
462 + FreeProgram(FinishCreatingProgram(acc));
463 + XtFree((char *)acc);
464 return NULL;
467 /* get the newly created program */
468 - prog = FinishCreatingProgram();
469 + prog = FinishCreatingProgram(acc);
470 + XtFree((char *)acc);
472 /* parse succeeded */
473 *msg = "";
474 @@ -550,9 +603,8 @@ static int yylex(void)
475 return NUMBER;
478 - /* process symbol tokens. "define" is a special case not handled
479 - by this parser, considered end of input. Another special case
480 - is action routine names which are allowed to contain '-' despite
481 + /* process symbol tokens. A special case are action
482 + routine names which are allowed to contain '-' despite
483 the ambiguity, handled in matchesActionRoutine. */
484 if (isalpha((unsigned char)*InPtr) || *InPtr == '$') {
485 if ((s=matchesActionRoutine(&InPtr)) == NULL) {
486 @@ -575,10 +627,7 @@ static int yylex(void)
487 if (!strcmp(symName, "in")) return IN;
488 if (!strcmp(symName, "$args")) return ARG_LOOKUP;
489 if (!strcmp(symName, "delete") && follow_non_whitespace('(', SYMBOL, DELETE) == DELETE) return DELETE;
490 - if (!strcmp(symName, "define")) {
491 - InPtr -= 6;
492 - return 0;
494 + if (!strcmp(symName, "define")) return DEFINE;
495 if ((s=LookupSymbol(symName)) == NULL) {
496 s = InstallSymbol(symName, symName[0]=='$' ?
497 (((symName[1] > '0' && symName[1] <= '9') && symName[2] == 0) ?
498 diff --quilt old/source/smartIndent.c new/source/smartIndent.c
499 --- old/source/smartIndent.c
500 +++ new/source/smartIndent.c
501 @@ -747,7 +747,7 @@ void BeginSmartIndent(WindowInfo *window
502 winData->inNewLineMacro = 0;
503 winData->inModMacro = 0;
504 winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
505 - &stoppedAt);
506 + &stoppedAt, False);
507 if (winData->newlineMacro == NULL) {
508 ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
509 "newline macro", errMsg);
510 @@ -757,7 +757,7 @@ void BeginSmartIndent(WindowInfo *window
511 winData->modMacro = NULL;
512 else {
513 winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
514 - &stoppedAt);
515 + &stoppedAt, False);
516 if (winData->modMacro == NULL) {
517 ParseError(window->shell, indentMacros->modMacro, stoppedAt,
518 "smart indent modify macro", errMsg);
519 @@ -1437,7 +1437,7 @@ static int checkSmartIndentDialogData(vo
522 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
523 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
524 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
525 if (prog == NULL) {
526 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
527 "newline macro", errMsg);
528 @@ -1453,7 +1453,7 @@ static int checkSmartIndentDialogData(vo
529 /* Test compile the modify macro */
530 if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
531 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
532 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
533 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
534 if (prog == NULL) {
535 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
536 "modify macro", errMsg);
537 diff --quilt old/source/userCmds.c new/source/userCmds.c
538 --- old/source/userCmds.c
539 +++ new/source/userCmds.c
540 @@ -2080,7 +2080,7 @@ static int checkMacroText(char *macro, W
541 Program *prog;
542 char *errMsg, *stoppedAt;
544 - prog = ParseMacro(macro, &errMsg, &stoppedAt);
545 + prog = ParseMacro(macro, &errMsg, &stoppedAt, False);
546 if (prog == NULL) {
547 if (errorParent != NULL) {
548 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
549 @@ -3019,7 +3019,7 @@ static char *copyMacroToEnd(char **inPtr
552 /* Parse the input */
553 - prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
554 + prog = ParseMacro(*inPtr, &errMsg, &stoppedAt, False);
555 if (prog == NULL) {
556 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
557 return NULL;