CVS update
[nedit-bw.git] / parse-define.patch
blob2894ad156faad8fb48a74515bc16ca1453ed54cc
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:
11 a = 0
13 define t {
16 t_print(a "\n")
18 ChangeLog:
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 | 144 +++++++++------------------------------------------
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, 128 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(Inst) * PROGRAM_SIZE);
64 + acc->progP = ProgP;
65 + memcpy(acc->loopStack, LoopStack, sizeof(Inst) * 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(Inst) * PROGRAM_SIZE);
95 + ProgP = acc->progP;
96 + memcpy(LoopStack, acc->loopStack, sizeof(Inst) * 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,36 @@ 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 + if (runWindow->macroCmdData == NULL) {
290 + /* runMacro() is responsable for freeing prog */
291 + runMacro(runWindow, prog);
292 + while (runWindow->macroCmdData != NULL) {
293 + XtAppNextEvent(XtWidgetToApplicationContext(runWindow->shell),
294 + &nextEvent);
295 + ServerDispatchEvent(&nextEvent);
298 + else {
299 + RunMacroAsSubrCall(prog);
302 + else {
303 + /* we are in 'parse only' mode, therefore release the prog */
304 + FreeProgram(prog);
307 - /* This stack is empty, so just free it without checking the members. */
308 - XtFree((char*) progStack);
310 return True;
312 @@ -1230,7 +1140,7 @@ void DoMacro(WindowInfo *window, const c
313 tMacro[macroLen+1] = '\0';
315 /* Parse the macro and report errors if it fails */
316 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
317 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
318 if (prog == NULL) {
319 ParseError(window->shell, tMacro, stoppedAt, errInName, errMsg);
320 XtFree(tMacro);
321 @@ -1494,7 +1404,7 @@ selEnd += $text_length - startLength\n}\
322 sprintf(loopedCmd, loopMacro, how, command);
324 /* Parse the resulting macro into an executable program "prog" */
325 - prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt);
326 + prog = ParseMacro(loopedCmd, &errMsg, &stoppedAt, False);
327 if (prog == NULL) {
328 fprintf(stderr, "NEdit internal error, repeat macro syntax wrong: %s\n",
329 errMsg);
330 diff --quilt old/source/nedit.c new/source/nedit.c
331 --- old/source/nedit.c
332 +++ new/source/nedit.c
333 @@ -832,7 +832,7 @@ static int checkDoMacroArg(const char *m
334 tMacro[macroLen+1] = '\0';
336 /* Do a test parse */
337 - prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
338 + prog = ParseMacro(tMacro, &errMsg, &stoppedAt, False);
339 XtFree(tMacro);
340 if (prog == NULL) {
341 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
342 diff --quilt old/source/parse.h new/source/parse.h
343 --- old/source/parse.h
344 +++ new/source/parse.h
345 @@ -30,6 +30,6 @@
347 #include "interpret.h"
349 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt);
350 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine);
352 #endif /* NEDIT_PARSE_H_INCLUDED */
353 diff --quilt old/source/parse.y new/source/parse.y
354 --- old/source/parse.y
355 +++ new/source/parse.y
356 @@ -75,16 +75,20 @@ static char *InPtr;
357 extern Inst *LoopStack[]; /* addresses of break, cont stmts */
358 extern Inst **LoopStackPtr; /* to fill at the end of a loop */
360 +static int AllowDefine;
364 %union {
365 Symbol *sym;
366 Inst *inst;
367 int nArgs;
368 + AccumulatorData *acc;
370 %token <sym> NUMBER STRING SYMBOL
371 %token DELETE ARG_LOOKUP
372 %token IF WHILE ELSE FOR BREAK CONTINUE RETURN
373 +%token <acc> DEFINE
374 %type <nArgs> arglist
375 %type <inst> cond comastmts for while else and or arrayexpr
376 %type <sym> evalsym
377 @@ -111,7 +115,7 @@ extern Inst **LoopStackPtr; /* to fill
379 %% /* Rules */
381 -program: blank stmts {
382 +program: blank allstmts {
383 ADD_OP(OP_RETURN_NO_VAL); return 0;
385 | blank '{' blank stmts '}' {
386 @@ -124,13 +128,56 @@ program: blank stmts {
387 return 1;
390 -block: '{' blank stmts '}' blank
391 +blockwb: '{' blank stmts '}' blank
392 | '{' blank '}' blank
394 +block: blockwb
395 | stmt
397 stmts: stmt
398 | stmts stmt
400 +allstmts: allstmt
401 + | allstmts allstmt
403 +allstmt: stmt
404 + | define
406 +define: DEFINE {
407 + /* bail out early, in case of unallowed "define" */
408 + if (!AllowDefine) {
409 + yyerror("macro definitions not allowed"); YYERROR;
412 + blank SYMBOL {
413 + $1 = (AccumulatorData *)XtMalloc(sizeof(AccumulatorData));
414 + /* we can't really be sure, that we not overwrite any
415 + ** wrong symbol
416 + **
417 + ** we should only overwrite installed MACRO_FUNCTION_SYM
418 + ** and this is questionable.
419 + */
420 + if ($4->type == MACRO_FUNCTION_SYM) {
421 + FreeProgram($4->value.val.prog);
423 + else if ($4->type == LOCAL_SYM ||
424 + $4->type == GLOBAL_SYM) {
425 + /* newly created sym, or we overwrite a local sym */;
426 + } else {
427 + yyerror("try to override built-in subroutine"); YYERROR;
429 + $4 = PromoteToGlobal($4);
430 + BeginCreatingProgram($1);
432 + blank blockwb {
433 + ADD_OP(OP_RETURN_NO_VAL);
434 + Program *prog = FinishCreatingProgram($1);
435 + XtFree((char *)$1);
436 + $4->type = MACRO_FUNCTION_SYM;
437 + $4->value.tag = NO_TAG;
438 + $4->value.val.prog = prog;
441 stmt: simpstmt '\n' blank
442 | IF '(' cond ')' blank block %prec IF_NO_ELSE {
443 SET_BR_OFF($3, GetPC());
444 @@ -474,11 +521,15 @@ blank: /* nothing */
445 ** as a pointer to a static string in msg, and the length of the string up
446 ** to where parsing failed in stoppedAt.
448 -Program *ParseMacro(char *expr, char **msg, char **stoppedAt)
449 +Program *ParseMacro(char *expr, char **msg, char **stoppedAt, int allowDefine)
451 Program *prog;
452 + AccumulatorData *acc = (AccumulatorData *)XtMalloc(sizeof(*acc));
454 + BeginCreatingProgram(acc);
456 - BeginCreatingProgram();
457 + /* whether we allow the "define" keyword */
458 + AllowDefine = allowDefine;
460 /* call yyparse to parse the string and check for success. If the parse
461 failed, return the error message and string index (the grammar aborts
462 @@ -487,12 +538,14 @@ Program *ParseMacro(char *expr, char **m
463 if (yyparse()) {
464 *msg = ErrMsg;
465 *stoppedAt = InPtr;
466 - FreeProgram(FinishCreatingProgram());
467 + FreeProgram(FinishCreatingProgram(acc));
468 + XtFree((char *)acc);
469 return NULL;
472 /* get the newly created program */
473 - prog = FinishCreatingProgram();
474 + prog = FinishCreatingProgram(acc);
475 + XtFree((char *)acc);
477 /* parse succeeded */
478 *msg = "";
479 @@ -550,9 +603,8 @@ static int yylex(void)
480 return NUMBER;
483 - /* process symbol tokens. "define" is a special case not handled
484 - by this parser, considered end of input. Another special case
485 - is action routine names which are allowed to contain '-' despite
486 + /* process symbol tokens. A special case are action
487 + routine names which are allowed to contain '-' despite
488 the ambiguity, handled in matchesActionRoutine. */
489 if (isalpha((unsigned char)*InPtr) || *InPtr == '$') {
490 if ((s=matchesActionRoutine(&InPtr)) == NULL) {
491 @@ -575,10 +627,7 @@ static int yylex(void)
492 if (!strcmp(symName, "in")) return IN;
493 if (!strcmp(symName, "$args")) return ARG_LOOKUP;
494 if (!strcmp(symName, "delete") && follow_non_whitespace('(', SYMBOL, DELETE) == DELETE) return DELETE;
495 - if (!strcmp(symName, "define")) {
496 - InPtr -= 6;
497 - return 0;
499 + if (!strcmp(symName, "define")) return DEFINE;
500 if ((s=LookupSymbol(symName)) == NULL) {
501 s = InstallSymbol(symName, symName[0]=='$' ?
502 (((symName[1] > '0' && symName[1] <= '9') && symName[2] == 0) ?
503 diff --quilt old/source/smartIndent.c new/source/smartIndent.c
504 --- old/source/smartIndent.c
505 +++ new/source/smartIndent.c
506 @@ -747,7 +747,7 @@ void BeginSmartIndent(WindowInfo *window
507 winData->inNewLineMacro = 0;
508 winData->inModMacro = 0;
509 winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
510 - &stoppedAt);
511 + &stoppedAt, False);
512 if (winData->newlineMacro == NULL) {
513 ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
514 "newline macro", errMsg);
515 @@ -757,7 +757,7 @@ void BeginSmartIndent(WindowInfo *window
516 winData->modMacro = NULL;
517 else {
518 winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
519 - &stoppedAt);
520 + &stoppedAt, False);
521 if (winData->modMacro == NULL) {
522 ParseError(window->shell, indentMacros->modMacro, stoppedAt,
523 "smart indent modify macro", errMsg);
524 @@ -1437,7 +1437,7 @@ static int checkSmartIndentDialogData(vo
527 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
528 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
529 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
530 if (prog == NULL) {
531 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
532 "newline macro", errMsg);
533 @@ -1453,7 +1453,7 @@ static int checkSmartIndentDialogData(vo
534 /* Test compile the modify macro */
535 if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
536 widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
537 - prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
538 + prog = ParseMacro(widgetText, &errMsg, &stoppedAt, False);
539 if (prog == NULL) {
540 ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
541 "modify macro", errMsg);
542 diff --quilt old/source/userCmds.c new/source/userCmds.c
543 --- old/source/userCmds.c
544 +++ new/source/userCmds.c
545 @@ -2080,7 +2080,7 @@ static int checkMacroText(char *macro, W
546 Program *prog;
547 char *errMsg, *stoppedAt;
549 - prog = ParseMacro(macro, &errMsg, &stoppedAt);
550 + prog = ParseMacro(macro, &errMsg, &stoppedAt, False);
551 if (prog == NULL) {
552 if (errorParent != NULL) {
553 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
554 @@ -3019,7 +3019,7 @@ static char *copyMacroToEnd(char **inPtr
557 /* Parse the input */
558 - prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
559 + prog = ParseMacro(*inPtr, &errMsg, &stoppedAt, False);
560 if (prog == NULL) {
561 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
562 return NULL;