]> git.sur5r.net Git - cc65/blob - src/ca65/macro.c
Use the new generic hash tables
[cc65] / src / ca65 / macro.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  macro.c                                  */
4 /*                                                                           */
5 /*                    Macros for the ca65 macroassembler                     */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38
39 /* common */
40 #include "check.h"
41 #include "hashstr.h"
42 #include "hashtab.h"
43 #include "xmalloc.h"
44
45 /* ca65 */
46 #include "condasm.h"
47 #include "error.h"
48 #include "global.h"
49 #include "istack.h"
50 #include "nexttok.h"
51 #include "pseudo.h"
52 #include "toklist.h"
53 #include "macro.h"
54
55
56
57 /*****************************************************************************/
58 /*                                 Forwards                                  */
59 /*****************************************************************************/
60
61
62
63 static unsigned HT_GenHash (const void* Key);
64 /* Generate the hash over a key. */
65
66 static const void* HT_GetKey (void* Entry);
67 /* Given a pointer to the user entry data, return a pointer to the key */
68
69 static HashNode* HT_GetHashNode (void* Entry);
70 /* Given a pointer to the user entry data, return a pointer to the hash node */
71
72 static int HT_Compare (const void* Key1, const void* Key2);
73 /* Compare two keys for equality */
74
75
76
77 /*****************************************************************************/
78 /*                                   Data                                    */
79 /*****************************************************************************/
80
81
82
83 /* Struct that describes an identifer (macro param, local list) */
84 typedef struct IdDesc IdDesc;
85 struct IdDesc {
86     IdDesc*         Next;       /* Linked list */
87     char            Id [1];     /* Identifier, dynamically allocated */
88 };
89
90
91
92 /* Struct that describes a macro definition */
93 typedef struct Macro Macro;
94 struct Macro {
95     HashNode        Node;       /* Hash list node */
96     Macro*          List;       /* List of all macros */
97     unsigned        LocalCount; /* Count of local symbols */
98     IdDesc*         Locals;     /* List of local symbols */
99     unsigned        ParamCount; /* Parameter count of macro */
100     IdDesc*         Params;     /* Identifiers of macro parameters */
101     unsigned        TokCount;   /* Number of tokens for this macro */
102     TokNode*        TokRoot;    /* Root of token list */
103     TokNode*        TokLast;    /* Pointer to last token in list */
104     unsigned char   Style;      /* Macro style */
105     char            Name [1];   /* Macro name, dynamically allocated */
106 };
107
108 /* Hash table functions */
109 static const HashFunctions HashFunc = {
110     HT_GenHash,
111     HT_GetKey,
112     HT_GetHashNode,
113     HT_Compare
114 };
115
116 /* Macro hash table */
117 static HashTable MacroTab = STATIC_HASHTABLE_INITIALIZER (117, &HashFunc);
118
119 /* Global macro data */
120 static Macro*   MacroRoot = 0;  /* List of all macros */
121
122 /* Structs that holds data for a macro expansion */
123 typedef struct MacExp MacExp;
124 struct MacExp {
125     MacExp*     Next;           /* Pointer to next expansion */
126     Macro*      M;              /* Which macro do we expand? */
127     unsigned    IfSP;           /* .IF stack pointer at start of expansion */
128     TokNode*    Exp;            /* Pointer to current token */
129     TokNode*    Final;          /* Pointer to final token */
130     unsigned    LocalStart;     /* Start of counter for local symbol names */
131     unsigned    ParamCount;     /* Number of actual parameters */
132     TokNode**   Params;         /* List of actual parameters */
133     TokNode*    ParamExp;       /* Node for expanding parameters */
134 };
135
136 /* Number of active macro expansions */
137 static unsigned MacExpansions = 0;
138
139 /* Flag if a macro expansion should get aborted */
140 static int DoMacAbort = 0;
141
142 /* Counter to create local names for symbols */
143 static unsigned LocalName = 0;
144
145
146
147 /*****************************************************************************/
148 /*                           Hash table functions                            */
149 /*****************************************************************************/
150
151
152
153 static unsigned HT_GenHash (const void* Key)
154 /* Generate the hash over a key. */
155 {
156     return HashStr (Key);
157 }
158
159
160
161 static const void* HT_GetKey (void* Entry)
162 /* Given a pointer to the user entry data, return a pointer to the index */
163 {
164     return ((Macro*) Entry)->Name;
165 }
166
167
168
169 static HashNode* HT_GetHashNode (void* Entry)
170 /* Given a pointer to the user entry data, return a pointer to the hash node */
171 {
172     return &((Macro*) Entry)->Node;
173 }
174
175
176
177 static int HT_Compare (const void* Key1, const void* Key2)
178 /* Compare two keys for equality */
179 {
180     return (strcmp (Key1, Key2) == 0);
181 }
182
183
184
185 /*****************************************************************************/
186 /*                                   Code                                    */
187 /*****************************************************************************/
188
189
190
191 static IdDesc* NewIdDesc (const char* Id)
192 /* Create a new IdDesc, initialize and return it */
193 {
194     /* Allocate memory */
195     unsigned Len = strlen (Id);
196     IdDesc* I = xmalloc (sizeof (IdDesc) + Len);
197
198     /* Initialize the struct */
199     I->Next = 0;
200     memcpy (I->Id, Id, Len);
201     I->Id [Len] = '\0';
202
203     /* Return the new struct */
204     return I;
205 }
206
207
208
209 static Macro* NewMacro (const char* Name, unsigned char Style)
210 /* Generate a new macro entry, initialize and return it */
211 {
212     /* Allocate memory */
213     unsigned Len = strlen (Name);
214     Macro* M = xmalloc (sizeof (Macro) + Len);
215
216     /* Initialize the macro struct */
217     InitHashNode (&M->Node, M);
218     M->LocalCount = 0;
219     M->Locals     = 0;
220     M->ParamCount = 0;
221     M->Params     = 0;
222     M->TokCount   = 0;
223     M->TokRoot    = 0;
224     M->TokLast    = 0;
225     M->Style      = Style;
226     memcpy (M->Name, Name, Len+1);
227
228     /* Insert the macro into the global macro list */
229     M->List = MacroRoot;
230     MacroRoot = M;
231
232     /* Insert the macro into the hash table */
233     HT_Insert (&MacroTab, &M->Node);
234
235     /* Return the new macro struct */
236     return M;
237 }
238
239
240
241 static MacExp* NewMacExp (Macro* M)
242 /* Create a new expansion structure for the given macro */
243 {
244     unsigned I;
245
246     /* Allocate memory */
247     MacExp* E = xmalloc (sizeof (MacExp));
248
249     /* Initialize the data */
250     E->M          = M;
251     E->IfSP       = GetIfStack ();
252     E->Exp        = M->TokRoot;
253     E->Final      = 0;
254     E->LocalStart = LocalName;
255     LocalName    += M->LocalCount;
256     E->ParamCount = 0;
257     E->Params     = xmalloc (M->ParamCount * sizeof (TokNode*));
258     E->ParamExp   = 0;
259     for (I = 0; I < M->ParamCount; ++I) {
260         E->Params [I] = 0;
261     }
262
263     /* One macro expansion more */
264     ++MacExpansions;
265
266     /* Return the new macro expansion */
267     return E;
268 }
269
270
271
272 static void FreeMacExp (MacExp* E)
273 /* Remove and free the current macro expansion */
274 {
275     unsigned I;
276
277     /* One macro expansion less */
278     --MacExpansions;
279
280     /* Free the parameter list */
281     for (I = 0; I < E->ParamCount; ++I) {
282         xfree (E->Params [I]);
283     }
284     xfree (E->Params);
285
286     /* Free the final token if we have one */
287     if (E->Final) {
288         FreeTokNode (E->Final);
289     }
290
291     /* Free the structure itself */
292     xfree (E);
293 }
294
295
296
297 static void MacSkipDef (unsigned Style)
298 /* Skip a macro definition */
299 {
300     if (Style == MAC_STYLE_CLASSIC) {
301         /* Skip tokens until we reach the final .endmacro */
302         while (Tok != TOK_ENDMACRO && Tok != TOK_EOF) {
303             NextTok ();
304         }
305         if (Tok != TOK_EOF) {
306             SkipUntilSep ();
307         } else {
308             Error (ERR_ENDMACRO_EXPECTED);
309         }
310     } else {
311         /* Skip until end of line */
312         SkipUntilSep ();
313     }
314 }
315
316
317
318 void MacDef (unsigned Style)
319 /* Parse a macro definition */
320 {
321     Macro* M;
322     TokNode* T;
323     int HaveParams;
324
325     /* We expect a macro name here */
326     if (Tok != TOK_IDENT) {
327         Error (ERR_IDENT_EXPECTED);
328         MacSkipDef (Style);
329         return;
330     }
331
332     /* Did we already define that macro? */
333     if (HT_Find (&MacroTab, SVal) != 0) {
334         /* Macro is already defined */
335         Error (ERR_SYM_ALREADY_DEFINED, SVal);
336         /* Skip tokens until we reach the final .endmacro */
337         MacSkipDef (Style);
338         return;
339     }
340
341     /* Define the macro */
342     M = NewMacro (SVal, Style);
343
344     /* Switch to raw token mode and skip the macro name */
345     EnterRawTokenMode ();
346     NextTok ();
347
348     /* If we have a DEFINE style macro, we may have parameters in braces,
349      * otherwise we may have parameters without braces.
350      */
351     if (Style == MAC_STYLE_CLASSIC) {
352         HaveParams = 1;
353     } else {
354         if (Tok == TOK_LPAREN) {
355             HaveParams = 1;
356             NextTok ();
357         } else {
358             HaveParams = 0;
359         }
360     }
361
362     /* Parse the parameter list */
363     if (HaveParams) {
364
365         while (Tok == TOK_IDENT) {
366
367             /* Create a struct holding the identifier */
368             IdDesc* I = NewIdDesc (SVal);
369
370             /* Insert the struct into the list, checking for duplicate idents */
371             if (M->ParamCount == 0) {
372                 M->Params = I;
373             } else {
374                 IdDesc* List = M->Params;
375                 while (1) {
376                     if (strcmp (List->Id, SVal) == 0) {
377                         Error (ERR_SYM_ALREADY_DEFINED, SVal);
378                     }
379                     if (List->Next == 0) {
380                         break;
381                     } else {
382                         List = List->Next;
383                     }
384                 }
385                 List->Next = I;
386             }
387             ++M->ParamCount;
388
389             /* Skip the name */
390             NextTok ();
391
392             /* Maybe there are more params... */
393             if (Tok == TOK_COMMA) {
394                 NextTok ();
395             } else {
396                 break;
397             }
398         }
399     }
400
401     /* For class macros, we expect a separator token, for define style macros,
402      * we expect the closing paren.
403      */
404     if (Style == MAC_STYLE_CLASSIC) {
405         ConsumeSep ();
406     } else if (HaveParams) {
407         ConsumeRParen ();
408     }
409
410     /* Preparse the macro body. We will read the tokens until we reach end of
411      * file, or a .endmacro (or end of line for DEFINE style macros) and store
412      * them into an token list internal to the macro. For classic macros, there
413      * the .LOCAL command is detected and removed at this time.
414      */
415     while (1) {
416
417         /* Check for end of macro */
418         if (Style == MAC_STYLE_CLASSIC) {
419             /* In classic macros, only .endmacro is allowed */
420             if (Tok == TOK_ENDMACRO) {
421                 /* Done */
422                 break;
423             }
424             /* May not have end of file in a macro definition */
425             if (Tok == TOK_EOF) {
426                 Error (ERR_ENDMACRO_EXPECTED);
427                 goto Done;
428             }
429         } else {
430             /* Accept a newline or end of file for new style macros */
431             if (TokIsSep (Tok)) {
432                 break;
433             }
434         }
435
436         /* Check for a .LOCAL declaration */
437         if (Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) {
438
439             while (1) {
440
441                 IdDesc* I;
442
443                 /* Skip .local or comma */
444                 NextTok ();
445
446                 /* Need an identifer */
447                 if (Tok != TOK_IDENT) {
448                     Error (ERR_IDENT_EXPECTED);
449                     SkipUntilSep ();
450                     break;
451                 }
452
453                 /* Put the identifier into the locals list and skip it */
454                 I = NewIdDesc (SVal);
455                 I->Next = M->Locals;
456                 M->Locals = I;
457                 ++M->LocalCount;
458                 NextTok ();
459
460                 /* Check for end of list */
461                 if (Tok != TOK_COMMA) {
462                     break;
463                 }
464
465             }
466
467             /* We need end of line after the locals */
468             ConsumeSep ();
469             continue;
470         }
471
472         /* Create a token node for the current token */
473         T = NewTokNode ();
474
475         /* If the token is an ident, check if it is a local parameter */
476         if (Tok == TOK_IDENT) {
477             unsigned Count = 0;
478             IdDesc* I = M->Params;
479             while (I) {
480                 if (strcmp (I->Id, SVal) == 0) {
481                     /* Local param name, replace it */
482                     T->Tok  = TOK_MACPARAM;
483                     T->IVal = Count;
484                     break;
485                 }
486                 ++Count;
487                 I = I->Next;
488             }
489         }
490
491         /* Insert the new token in the list */
492         if (M->TokCount == 0) {
493             /* First token */
494             M->TokRoot = M->TokLast = T;
495         } else {
496             /* We have already tokens */
497             M->TokLast->Next = T;
498             M->TokLast = T;
499         }
500         ++M->TokCount;
501
502         /* Read the next token */
503         NextTok ();
504     }
505
506     /* Skip the .endmacro for a classic macro */
507     if (Style == MAC_STYLE_CLASSIC) {
508         NextTok ();
509     }
510
511 Done:
512     /* Switch out of raw token mode */
513     LeaveRawTokenMode ();
514 }
515
516
517
518 static int MacExpand (void* Data)
519 /* If we're currently expanding a macro, set the the scanner token and
520  * attribute to the next value and return true. If we are not expanding
521  * a macro, return false.
522  */
523 {
524     /* Cast the Data pointer to the actual data structure */
525     MacExp* Mac = (MacExp*) Data;
526
527     /* Check if we should abort this macro */
528     if (DoMacAbort) {
529
530         /* Reset the flag */
531         DoMacAbort = 0;
532
533         /* Abort any open .IF statements in this macro expansion */
534         CleanupIfStack (Mac->IfSP);
535
536         /* Terminate macro expansion */
537         goto MacEnd;
538     }
539
540     /* We're expanding a macro. Check if we are expanding one of the
541      * macro parameters.
542      */
543     if (Mac->ParamExp) {
544
545         /* Ok, use token from parameter list */
546         TokSet (Mac->ParamExp);
547
548         /* Set pointer to next token */
549         Mac->ParamExp = Mac->ParamExp->Next;
550
551         /* Done */
552         return 1;
553
554     }
555
556     /* We're not expanding macro parameters. Check if we have tokens left from
557      * the macro itself.
558      */
559     if (Mac->Exp) {
560
561         /* Use next macro token */
562         TokSet (Mac->Exp);
563
564         /* Set pointer to next token */
565         Mac->Exp = Mac->Exp->Next;
566
567         /* Is it a request for actual parameter count? */
568         if (Tok == TOK_PARAMCOUNT) {
569             Tok  = TOK_INTCON;
570             IVal = Mac->ParamCount;
571             return 1;
572         }
573
574         /* Is it the name of a macro parameter? */
575         if (Tok == TOK_MACPARAM) {
576
577             /* Start to expand the parameter token list */
578             Mac->ParamExp = Mac->Params [IVal];
579
580             /* Recursive call to expand the parameter */
581             return MacExpand (Mac);
582         }
583
584         /* If it's an identifier, it may in fact be a local symbol */
585         if (Tok == TOK_IDENT && Mac->M->LocalCount) {
586             /* Search for the local symbol in the list */
587             unsigned Index = 0;
588             IdDesc* I = Mac->M->Locals;
589             while (I) {
590                 if (strcmp (SVal, I->Id) == 0) {
591                     /* This is in fact a local symbol, change the name. Be sure
592                      * to generate a local label name if the original name was
593                      * a local label, and also generate a name that cannot be
594                      * generated by a user.
595                      */
596                     unsigned PrefixLen = (I->Id[0] == LocalStart);
597                     sprintf (SVal, "%.*sLOCAL-MACRO-SYMBOL-%04X", PrefixLen,
598                              I->Id, Mac->LocalStart + Index);
599                     break;
600                 }
601                 /* Next symbol */
602                 ++Index;
603                 I = I->Next;
604             }
605
606             /* Done */
607             return 1;
608         }
609
610         /* The token was successfully set */
611         return 1;
612
613     }
614
615     /* No more macro tokens. Do we have a final token? */
616     if (Mac->Final) {
617
618         /* Set the final token and remove it */
619         TokSet (Mac->Final);
620         FreeTokNode (Mac->Final);
621         Mac->Final = 0;
622
623         /* The token was successfully set */
624         return 1;
625
626     }
627
628 MacEnd:
629     /* End of macro expansion */
630     FreeMacExp (Mac);
631
632     /* Pop the input function */
633     PopInput ();
634
635     /* No token available */
636     return 0;
637 }
638
639
640
641 static void StartExpClassic (Macro* M)
642 /* Start expanding the classic macro M */
643 {
644     MacExp* E;
645
646     /* Skip the macro name */
647     NextTok ();
648
649     /* Create a structure holding expansion data */
650     E = NewMacExp (M);
651
652     /* Read the actual parameters */
653     while (!TokIsSep (Tok)) {
654
655         TokNode* Last;
656
657         /* Check for maximum parameter count */
658         if (E->ParamCount >= M->ParamCount) {
659             Error (ERR_TOO_MANY_PARAMS);
660             SkipUntilSep ();
661             break;
662         }
663
664         /* Read tokens for one parameter, accept empty params */
665         Last = 0;
666         while (Tok != TOK_COMMA && Tok != TOK_SEP) {
667
668             TokNode* T;
669
670             /* Check for end of file */
671             if (Tok == TOK_EOF) {
672                 Error (ERR_UNEXPECTED_EOF);
673                 return;
674             }
675
676             /* Get the next token in a node */
677             T = NewTokNode ();
678
679             /* Insert it into the list */
680             if (Last == 0) {
681                 E->Params [E->ParamCount] = T;
682             } else {
683                 Last->Next = T;
684             }
685             Last = T;
686
687             /* And skip it... */
688             NextTok ();
689         }
690
691         /* One parameter more */
692         ++E->ParamCount;
693
694         /* Check for a comma */
695         if (Tok == TOK_COMMA) {
696             NextTok ();
697         } else {
698             break;
699         }
700     }
701
702     /* Insert a new token input function */
703     PushInput (MacExpand, E, ".MACRO");
704 }
705
706
707
708 static void StartExpDefine (Macro* M)
709 /* Start expanding a DEFINE style macro */
710 {
711     /* Create a structure holding expansion data */
712     MacExp* E = NewMacExp (M);
713
714     /* A define style macro must be called with as many actual parameters
715      * as there are formal ones. Get the parameter count.
716      */
717     unsigned Count = M->ParamCount;
718
719     /* Skip the current token */
720     NextTok ();
721
722     /* Read the actual parameters */
723     while (Count--) {
724
725         TokNode* Last;
726
727         /* Check if there is really a parameter */
728         if (TokIsSep (Tok) || Tok == TOK_COMMA) {
729             Error (ERR_MACRO_PARAM_EXPECTED);
730             SkipUntilSep ();
731             return;
732         }
733
734         /* Read tokens for one parameter */
735         Last = 0;
736         do {
737
738             TokNode* T;
739
740             /* Get the next token in a node */
741             T = NewTokNode ();
742
743             /* Insert it into the list */
744             if (Last == 0) {
745                 E->Params [E->ParamCount] = T;
746             } else {
747                 Last->Next = T;
748             }
749             Last = T;
750
751             /* And skip it... */
752             NextTok ();
753
754         } while (Tok != TOK_COMMA && !TokIsSep (Tok));
755
756         /* One parameter more */
757         ++E->ParamCount;
758
759         /* Check for a comma */
760         if (Count > 0) {
761             if (Tok == TOK_COMMA) {
762                 NextTok ();
763             } else {
764                 Error (ERR_COMMA_EXPECTED);
765             }
766         }
767     }
768
769     /* Macro expansion will overwrite the current token. This is a problem
770      * for define style macros since these are called from the scanner level.
771      * To avoid it, remember the current token and re-insert it if macro
772      * expansion is done.
773      */
774     E->Final = NewTokNode ();
775
776     /* Insert a new token input function */
777     PushInput (MacExpand, E, ".DEFINE");
778 }
779
780
781
782 void MacExpandStart (void)
783 /* Start expanding the macro in SVal */
784 {
785     /* Search for the macro */
786     Macro* M = HT_FindEntry (&MacroTab, SVal);
787     CHECK (M != 0);
788
789     /* Call the apropriate subroutine */
790     switch (M->Style) {
791         case MAC_STYLE_CLASSIC: StartExpClassic (M);    break;
792         case MAC_STYLE_DEFINE:  StartExpDefine (M);     break;
793         default:                Internal ("Invalid macro style: %d", M->Style);
794     }
795 }
796
797
798
799 void MacAbort (void)
800 /* Abort the current macro expansion */
801 {
802     /* Must have an expansion */
803     CHECK (MacExpansions > 0);
804
805     /* Set a flag so macro expansion will terminate on the next call */
806     DoMacAbort = 1;
807 }
808
809
810
811 int IsMacro (const char* Name)
812 /* Return true if the given name is the name of a macro */
813 {
814     return (HT_Find (&MacroTab, Name) != 0);
815 }
816
817
818
819 int IsDefine (const char* Name)
820 /* Return true if the given name is the name of a define style macro */
821 {
822     Macro* M = HT_FindEntry (&MacroTab, Name);
823     return (M != 0 && M->Style == MAC_STYLE_DEFINE);
824 }
825
826
827
828 int InMacExpansion (void)
829 /* Return true if we're currently expanding a macro */
830 {
831     return (MacExpansions > 0);
832 }
833
834
835
836
837
838