]> Shamusworld >> Repos - rmac/blob - macro.c
Fixed ignored label on rept line (bug #18)
[rmac] / macro.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // MACRO.C - Macro Definition and Invocation
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source Utilised with the Kind Permission of Landon Dyer
7 //
8
9 #include "macro.h"
10 #include "debug.h"
11 #include "direct.h"
12 #include "error.h"
13 #include "expr.h"
14 #include "listing.h"
15 #include "procln.h"
16 #include "symbol.h"
17 #include "token.h"
18
19
20 LONG curuniq;                                                           // Current macro's unique number
21 //TOKEN ** argp;                                                                // Free spot in argptrs[]
22 int macnum;                                                                     // Unique number for macro definition
23 TOKEN * argPtrs[128];                                           // 128 arguments ought to be enough for anyone
24 static int argp;
25
26 static LONG macuniq;                                            // Unique-per-macro number
27 static SYM * curmac;                                            // Macro currently being defined
28 //static char ** curmln;                                                // Previous macro line (or NULL)
29 static VALUE argno;                                                     // Formal argument count 
30
31 static LONG * firstrpt;                                         // First .rept line 
32 static LONG * nextrpt;                                          // Last .rept line 
33 static int rptlevel;                                            // .rept nesting level 
34
35
36 //
37 // Initialize Macro Processor
38 //
39 void InitMacro(void)
40 {
41         macuniq = 0;
42         macnum = 1;
43 //      argp = NULL;
44         argp = 0;
45 }
46
47
48 //
49 // Exit from a Macro;
50 // -- pop any intervening include files and repeat blocks;
51 // -- restore argument stack;
52 // -- pop the macro.
53 //
54 int ExitMacro(void)
55 {
56 #warning !!! Bad macro exiting !!!
57 /*
58 This is a problem. Currently, the argument logic just keeps the current
59 arguments and doesn't save anything if a new macro is called in the middle
60 of another (nested macros). Need to fix that somehow.
61 */
62         // Pop intervening include files and .rept blocks
63         while (cur_inobj != NULL && cur_inobj->in_type != SRC_IMACRO)
64                 fpop();
65
66         if (cur_inobj == NULL)
67                 fatal("too many ENDMs");
68
69         // Restore
70         // o  old arg context
71         // o  old unique number
72         // ...and then pop the macro.
73
74         IMACRO * imacro = cur_inobj->inobj.imacro;
75         curuniq = imacro->im_olduniq;
76
77 //      /*TOKEN ** p = */argp--;
78 //      argp = (TOKEN **)*argp;
79         DEBUG printf("ExitMacro: argp: %d -> ", argp);
80         argp -= imacro->im_nargs;
81         DEBUG printf("%d (nargs = %d)\n", argp, imacro->im_nargs);
82
83         fpop();
84         return 0;
85 }
86
87
88 //
89 // Add a Formal Argument to a Macro Definition
90 //
91 int defmac2(char * argname)
92 {
93         SYM * arg;
94
95         if (lookup(argname, MACARG, (int)curmac->sattr) != NULL)
96                 return error("multiple formal argument definition");
97
98         arg = NewSymbol(argname, MACARG, (int)curmac->sattr);
99         arg->svalue = argno++;
100
101         return OK;
102 }
103
104
105 //
106 // Add a line to a macro definition; also print lines to listing file (if
107 // enabled). The last line of the macro (containing .endm) is not included in
108 // the macro. A label on that line will be lost.
109 // notEndFlag is -1 for all lines but the last one (.endm), when it is 0.
110 //
111 int defmac1(char * ln, int notEndFlag)
112 {
113         PTR p;
114         LONG len;
115
116         if (list_flag)
117         {
118                 listeol();                                                      // Flush previous source line
119                 lstout('.');                                            // Mark macro definition with period
120         }
121
122         // This is just wrong, wrong, wrong. It makes up a weird kind of string with
123         // a pointer on front, and then uses a ** to manage them: This is a recipe
124         // for disaster.
125         // How to manage it then?
126         // Could use a linked list, like Landon uses everywhere else.
127 /*
128 How it works:
129 Allocate a space big enough for the string + NULL + pointer.
130 Set the pointer to NULL.
131 Copy the string to the space after the pointer.
132 If this is the 1st time through, set the SYM * "svalue" to the pointer.
133 If this is the 2nd time through, derefence the ** to point to the memory you just allocated.
134 Then, set the ** to the location of the memory you allocated for the next pass through.
135
136 This is a really low level way to do a linked list, and by bypassing all the safety
137 features of the language. Seems like we can do better here.
138 */
139         if (notEndFlag)
140         {
141 #if 0
142                 len = strlen(ln) + 1 + sizeof(LONG);
143                 p.cp = malloc(len);
144                 *p.lp = 0;
145                 strcpy(p.cp + sizeof(LONG), ln);
146
147                 // Link line of text onto end of list
148                 if (curmln == NULL)
149                         curmac->svalue = p.cp;
150                 else
151                         *curmln = p.cp;
152
153                 curmln = (char **)p.cp;
154                 return 1;                                                       // Keep looking 
155 #else
156                 if (curmac->lineList == NULL)
157                 {
158                         curmac->lineList = malloc(sizeof(struct LineList));
159                         curmac->lineList->next = NULL;
160                         curmac->lineList->line = strdup(ln);
161                         curmac->last = curmac->lineList;
162                 }
163                 else
164                 {
165                         curmac->last->next = malloc(sizeof(struct LineList));
166                         curmac->last->next->next = NULL;
167                         curmac->last->next->line = strdup(ln);
168                         curmac->last = curmac->last->next;
169                 }
170
171                 return 1;                                                       // Keep looking
172 #endif
173         }
174
175         return 0;                                                               // Stop looking at the end
176 }
177
178
179 //
180 // Define macro
181 //
182 // macro foo arg1,arg2,...
183 //    :
184 //     :
185 //    endm
186 //
187 //  Helper functions:
188 //  -----------------
189 //  `defmac1' adds lines of text to the macro definition
190 //  `defmac2' processes the formal arguments (and sticks them into the symbol table)
191 //
192 int DefineMacro(void)
193 {
194         // Setup entry in symbol table, make sure the macro isn't a duplicate
195         // entry, and that it doesn't override any processor mnemonic or assembler
196         // directive.
197         if (*tok++ != SYMBOL)
198                 return error("missing symbol");
199
200         char * name = string[*tok++];
201
202         if (lookup(name, MACRO, 0) != NULL)
203                 return error("duplicate macro definition");
204
205         curmac = NewSymbol(name, MACRO, 0);
206         curmac->svalue = 0;
207         curmac->sattr = (WORD)(macnum++);
208
209         // Parse and define formal arguments in symbol table
210         if (*tok != EOL)
211         {
212                 argno = 0;
213                 symlist(defmac2);
214                 at_eol();
215         }
216
217         // Suck in the macro definition; we're looking for an ENDM symbol on a line
218         // by itself to terminate the definition.
219 //      curmln = NULL;
220         curmac->lineList = NULL;
221         lncatch(defmac1, "endm ");
222
223         return 0;
224 }
225
226
227 //
228 // Add lines to a .rept definition
229 //
230 int defr1(char * ln, int kwno)
231 {
232         LONG len;
233         LONG * p;
234
235         if (list_flag)
236         {
237                 listeol();                                                              // Flush previous source line
238                 lstout('#');                                                    // Mark this a 'rept' block
239         }
240
241         switch (kwno)
242         {
243         case 0:                                                                         // .endr 
244                 if (--rptlevel == 0)
245                 return(0);
246                 goto addln;
247         case 1:                                                                         // .rept 
248                 rptlevel++;
249         default:
250 //MORE stupidity here...
251 #warning "!!! Casting (char *) as LONG !!!"
252         addln:
253                 // Allocate length of line + 1('\0') + LONG
254                 len = strlen(ln) + 1 + sizeof(LONG);
255 //              p = (LONG *)amem(len);
256                 p = (LONG *)malloc(len);
257                 *p = 0;
258
259                 strcpy((char *)(p + 1), ln);
260                 
261                 if (nextrpt == NULL)
262                 {
263                         firstrpt = p;           // First line of rept statement
264                 }
265                 else
266                 {
267                         *nextrpt = (LONG)p;
268                 }
269
270                 nextrpt = p;
271
272                 return rptlevel;
273         }
274 }
275
276
277 //
278 // Define a .rept block, this gets hairy because they can be nested
279 //
280 int defrept(void)
281 {
282         INOBJ * inobj;
283         IREPT * irept;
284         VALUE eval;
285
286         // Evaluate repeat expression
287         if (abs_expr(&eval) != OK)
288                 return ERROR;
289
290         // Suck in lines for .rept block
291         firstrpt = NULL;
292         nextrpt = NULL;
293         rptlevel = 1;
294         lncatch(defr1, "endr rept ");
295
296         // Alloc and init input object
297         if (firstrpt)
298         {
299                 inobj = a_inobj(SRC_IREPT);                             // Create a new REPT input object
300                 irept = inobj->inobj.irept;
301                 irept->ir_firstln = firstrpt;
302                 irept->ir_nextln = NULL;
303                 irept->ir_count = eval;
304         }
305
306         return 0;
307 }
308
309
310 //
311 // Hand off lines of text to the function `lnfunc' until a line containing one
312 // of the directives in `dirlist' is encountered. Return the number of the
313 // keyword encountered (0..n)
314 // 
315 // `dirlist' contains null-seperated terminated keywords.  A final null
316 // terminates the list. Directives are compared to the keywords without regard
317 // to case.
318 // 
319 // If `lnfunc' is NULL, then lines are simply skipped.
320 // If `lnfunc' returns an error, processing is stopped.
321 // 
322 // `lnfunc' is called with an argument of -1 for every line but the last one,
323 // when it is called with an argument of the keyword number that caused the
324 // match.
325 //
326 int lncatch(int (* lnfunc)(), char * dirlist)
327 {
328         char * p;
329         int k;
330
331         if (lnfunc != NULL)
332                 lnsave++;                                                               // Tell tokenizer to keep lines 
333
334         for(;;)
335         {
336                 if (TokenizeLine() == TKEOF)
337                 {
338                         errors("encountered end-of-file looking for '%s'", dirlist);
339                         fatal("cannot continue");
340                 }
341
342                 // Test for end condition.  Two cases to handle:
343                 //            <directive>
344                 //    symbol: <directive>
345                 p = NULL;
346                 k = -1;
347
348                 if (*tok == SYMBOL)
349                 {
350                         if ((tok[2] == ':' || tok[2] == DCOLON))
351                         {
352                                 if (tok[3] == SYMBOL)                   // label: symbol
353                                         p = string[tok[4]];
354                         }
355                         else
356                         {
357                                 p = string[tok[1]];                             // Symbol
358                         }
359                 }
360
361                 if (p != NULL)
362                 {
363                         if (*p == '.')                                          // ignore leading '.'s 
364                                 p++;
365
366                         k = kwmatch(p, dirlist);
367                 }
368
369                 // Hand-off line to function
370                 // if it returns 0, and we found a keyword, stop looking.
371                 // if it returns 1, hand off the line and keep looking.
372                 if (lnfunc != NULL)
373                         k = (*lnfunc)(lnbuf, k);
374
375                 if (!k)
376                         break;
377         }
378
379         if (lnfunc != NULL)
380                 lnsave--;                                                               // Tell tokenizer to stop keeping lines
381
382         return 0;
383 }
384
385
386 //
387 // See if the string `kw' matches one of the keywords in `kwlist'.  If so,
388 // return the number of the keyword matched.  Return -1 if there was no match.
389 // Strings are compared without regard for case.
390 //
391 int kwmatch(char * kw, char * kwlist)
392 {
393         char * p;
394         char c1;
395         char c2;
396         int k;
397
398         for(k=0; *kwlist; ++k)
399         {
400                 for(p=kw;;)
401                 {
402                         c1 = *kwlist++;
403                         c2 = *p++;
404
405                         if (c2 >= 'A' && c2 <= 'Z')
406                                 c2 += 32;
407
408                         if (c1 == ' ' && c2 == EOS)
409                                 return k;
410
411                         if (c1 != c2)
412                                 break;
413                 }
414
415                 // Skip to beginning of next keyword in `kwlist'
416                 while (*kwlist && *kwlist != ' ')
417                         ++kwlist;
418
419                 if (*kwlist== ' ')
420                         kwlist++;
421         }
422
423         return -1;
424 }
425
426
427 //
428 // Invoke a macro
429 // o  parse, count and copy arguments
430 // o  push macro's string-stream
431 //
432 int InvokeMacro(SYM * mac, WORD siz)
433 {
434         TOKEN * p = NULL;
435         int dry_run;
436         WORD arg_siz = 0;
437 //      TOKEN ** argptr = NULL;
438 //Doesn't need to be global! (or does it???--it does)
439 //      argp = 0;
440         DEBUG printf("InvokeMacro: argp: %d -> ", argp);
441
442         INOBJ * inobj = a_inobj(SRC_IMACRO);            // Alloc and init IMACRO 
443         IMACRO * imacro = inobj->inobj.imacro;
444         imacro->im_siz = siz;
445         WORD nargs = 0;
446         TOKEN * beg_tok = tok;                                          // 'tok' comes from token.c
447         TOKEN * startOfArg;
448         TOKEN * dest;
449         int stringNum = 0;
450         int argumentNum = 0;
451         int i;
452
453         for(dry_run=1; ; dry_run--)
454         {
455                 for(tok=beg_tok; *tok!=EOL;)
456                 {
457                         if (dry_run)
458                                 nargs++;
459                         else
460                         {
461 #if 0                           
462                                 *argptr++ = p;
463 #else
464                                 argPtrs[argp++] = p;
465                                 startOfArg = p;
466 #endif
467                         }
468
469                         // Keep going while tok isn't pointing at a comma or EOL
470                         while (*tok != ',' && *tok != EOL)
471                         {
472                                 // Skip over backslash character, unless it's followed by an EOL
473                                 if (*tok == '\\' && tok[1] != EOL)
474                                         tok++;
475
476                                 switch (*tok)
477                                 {
478                                 case CONST:
479                                 case SYMBOL:
480 //Shamus: Possible bug. ACONST has 2 tokens after it, not just 1
481                                 case ACONST:
482                                         if (dry_run)
483                                         {
484                                                 arg_siz += sizeof(TOKEN);
485                                                 tok++;
486                                         }
487                                         else
488                                         {
489                                                 *p++ = *tok++;
490                                         }
491                                 // FALLTHROUGH (picks up the arg after a CONST, SYMBOL or ACONST)
492                                 default:
493                                         if (dry_run)
494                                         {
495                                                 arg_siz += sizeof(TOKEN);
496                                                 tok++;
497                                         }
498                                         else
499                                         {
500                                                 *p++ = *tok++;
501                                         }
502
503                                         break;
504                                 }
505                         }
506
507                         // We hit the comma or EOL, so count/stuff it
508                         if (dry_run)
509                                 arg_siz += sizeof(TOKEN);
510                         else
511                                 *p++ = EOL;
512
513                         // If we hit the comma instead of an EOL, skip over it
514                         if (*tok == ',')
515                                 tok++;
516
517                         // Do our QnD token grabbing (this will be redone once we get all
518                         // the data structures fixed as this is a really dirty hack)
519                         if (!dry_run)
520                         {
521                                 dest = imacro->argument[argumentNum].token;
522                                 stringNum = 0;
523
524                                 do
525                                 {
526                                         // Remap strings to point the IMACRO internal token storage
527                                         if (*startOfArg == SYMBOL || *startOfArg == STRING)
528                                         {
529                                                 *dest++ = *startOfArg++;
530                                                 imacro->argument[argumentNum].string[stringNum] = strdup(string[*startOfArg++]);
531                                                 *dest++ = stringNum++;
532                                         }
533                                         else
534                                                 *dest++ = *startOfArg++;
535                                 }
536                                 while (*startOfArg != EOL);
537
538                                 *dest = *startOfArg;            // Copy EOL...
539                                 argumentNum++;
540                         }
541                 }
542
543                 // Allocate space for argument ptrs and so on and then go back and
544                 // construct the arg frame
545                 if (dry_run)
546                 {
547                         if (nargs != 0)
548                                 p = (TOKEN *)malloc(arg_siz);
549 //                              p = (TOKEN *)malloc(arg_siz + sizeof(TOKEN));
550
551 /*
552 Shamus:
553 This construct is meant to deal with nested macros, so the simple minded way
554 we deal with them now won't work. :-/ Have to think about how to fix.
555 What we could do is simply move the argp with each call, and move it back by the
556 number of arguments in the macro that's ending. That would solve the problem nicely.
557 [Which we do now. But that uncovered another problem: the token strings are all
558 stale by the time a nested macro gets to the end. But they're supposed to be symbols,
559 which means if we put symbol references into the argument token streams, we can
560 alleviate this problem.]
561 */
562 #if 0
563                         argptr = (TOKEN **)malloc((nargs + 1) * sizeof(LONG));
564                         *argptr++ = (TOKEN *)argp;
565                         argp = argptr;
566 #else
567                         // We don't need to do anything here since we already advance argp
568                         // when parsing the arguments.
569 //                      argp += nargs;
570 #endif
571                 }
572                 else 
573                         break;
574         }
575
576         DEBUG printf("%d\n", argp);
577
578         // Setup imacro:
579         // o  # arguments;
580         // o  -> macro symbol;
581         // o  -> macro definition string list;
582         // o  save 'curuniq', to be restored when the macro pops;
583         // o  bump `macuniq' counter and set 'curuniq' to it;
584         imacro->im_nargs = nargs;
585         imacro->im_macro = mac;
586 //      imacro->im_nextln = (TOKEN *)mac->svalue;
587         imacro->im_nextln = mac->lineList;
588         imacro->im_olduniq = curuniq;
589         curuniq = macuniq++;
590         imacro->argBase = argp - nargs;                 // Shamus: keep track of argument base
591
592         DEBUG
593         {
594                 printf("nargs=%d\n", nargs);
595
596                 for(nargs=0; nargs<imacro->im_nargs; nargs++)
597                 {
598                         printf("arg%d=", nargs);
599 //                      dumptok(argp[imacro->im_nargs - nargs - 1]);
600 //                      dumptok(argPtrs[imacro->im_nargs - nargs - 1]);
601                         dumptok(argPtrs[(argp - imacro->im_nargs) + nargs]);
602                 }
603         }
604
605         return OK;
606 }
607