]> git.sur5r.net Git - cc65/blob - src/ca65/condasm.c
Use line infos to output more verbose error and warning messages whenever
[cc65] / src / ca65 / condasm.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 condasm.c                                 */
4 /*                                                                           */
5 /*                   Conditional assembly support for ca65                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2011, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 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 /* ca65 */
37 #include "error.h"
38 #include "expr.h"
39 #include "instr.h"
40 #include "lineinfo.h"
41 #include "nexttok.h"
42 #include "symbol.h"
43 #include "symtab.h"
44 #include "condasm.h"
45
46
47
48 /*****************************************************************************/
49 /*                                   Data                                    */
50 /*****************************************************************************/
51
52
53
54 /* Maximum count of nested .ifs */
55 #define MAX_IFS         256
56
57 /* Set of bitmapped flags for the if descriptor */
58 enum {
59     ifNone      = 0x0000,               /* No flag */
60     ifCond      = 0x0001,               /* IF condition was true */
61     ifElse      = 0x0002,               /* We had a .ELSE branch */
62     ifNeedTerm  = 0x0004                /* Need .ENDIF termination */
63 };
64
65
66
67 /*****************************************************************************/
68 /*                               struct IfDesc                               */
69 /*****************************************************************************/
70
71
72
73 /* One .IF descriptor */
74 typedef struct IfDesc IfDesc;
75 struct IfDesc {
76     unsigned    Flags;          /* Bitmapped flags, see above */
77     Collection  LineInfos;      /* File position of the .IF */
78     const char* Name;           /* Name of the directive */
79 };
80
81 /* The .IF stack */
82 static IfDesc IfStack [MAX_IFS];
83 static unsigned IfCount = 0;
84
85
86
87 static IfDesc* AllocIf (const char* Directive, int NeedTerm)
88 /* Alloc a new element from the .IF stack */
89 {
90     IfDesc* ID;
91
92     /* Check for stack overflow */
93     if (IfCount >= MAX_IFS) {
94         Fatal ("Too many nested .IFs");
95     }
96
97     /* Alloc one element */
98     ID = &IfStack [IfCount++];
99
100     /* Initialize elements */
101     ID->Flags     = NeedTerm? ifNeedTerm : ifNone;
102     ID->LineInfos = EmptyCollection;
103     GetFullLineInfo (&ID->LineInfos, 0);
104     ID->Name      = Directive;
105
106     /* Return the result */
107     return ID;
108 }
109
110
111
112 static IfDesc* GetCurrentIf (void)
113 /* Return the current .IF descriptor */
114 {
115     if (IfCount == 0) {
116         return 0;
117     } else {
118         return &IfStack [IfCount-1];
119     }
120 }
121
122
123
124 static void FreeIf (void)
125 /* Free all .IF descriptors until we reach one with the NeedTerm bit set */
126 {
127     int Done;
128     do {
129         IfDesc* D = GetCurrentIf();
130         if (D == 0) {
131             Error (" Unexpected .ENDIF");
132             Done = 1;
133         } else {
134             Done = (D->Flags & ifNeedTerm) != 0;
135             --IfCount;
136         }
137     } while (!Done);
138 }
139
140
141
142 static int GetCurrentIfCond (void)
143 /* Return the current condition based on all conditions on the stack */
144 {
145     unsigned Count;
146     for (Count = 0; Count < IfCount; ++Count) {
147         if ((IfStack[Count].Flags & ifCond) == 0) {
148             return 0;
149         }
150     }
151     return 1;
152 }
153
154
155
156 static void SetIfCond (IfDesc* ID, int C)
157 /* Set the .IF condition */
158 {
159     if (C) {
160         ID->Flags |= ifCond;
161     } else {
162         ID->Flags &= ~ifCond;
163     }
164 }
165
166
167
168 static void InvertIfCond (IfDesc* ID)
169 /* Invert the current condition */
170 {
171     ID->Flags ^= ifCond;
172 }
173
174
175
176 static int GetElse (const IfDesc* ID)
177 /* Return true if we had a .ELSE */
178 {
179     return (ID->Flags & ifElse) != 0;
180 }
181
182
183
184 static void SetElse (IfDesc* ID, int E)
185 /* Set the .ELSE flag */
186 {
187     if (E) {
188         ID->Flags |= ifElse;
189     } else {
190         ID->Flags &= ~ifElse;
191     }
192 }
193
194
195
196 /*****************************************************************************/
197 /*                                   Code                                    */
198 /*****************************************************************************/
199
200
201
202 void DoConditionals (void)
203 /* Catch all for conditional directives */
204 {
205     IfDesc* D;
206
207     int IfCond = GetCurrentIfCond ();
208     do {
209
210         switch (CurTok.Tok) {
211
212             case TOK_ELSE:
213                 D = GetCurrentIf ();
214                 if (D == 0) {
215                     Error ("Unexpected .ELSE");
216                 } else if (GetElse(D)) {
217                     /* We already had a .ELSE ! */
218                     Error ("Duplicate .ELSE");
219                 } else {
220                     /* Allow an .ELSE */
221                     InvertIfCond (D);
222                     SetElse (D, 1);
223                     GetFullLineInfo (&D->LineInfos, 0);
224                     D->Name = ".ELSE";
225                     IfCond = GetCurrentIfCond ();
226                 }
227                 NextTok ();
228                 ExpectSep ();
229                 break;
230
231             case TOK_ELSEIF:
232                 D = GetCurrentIf ();
233                 if (D == 0) {
234                     Error ("Unexpected .ELSEIF");
235                 } else if (GetElse(D)) {
236                     /* We already had a .ELSE */
237                     Error ("Duplicate .ELSE");
238                 } else {
239                     /* Handle as if there was an .ELSE first */
240                     InvertIfCond (D);
241                     SetElse (D, 1);
242
243                     /* Allocate and prepare a new descriptor */
244                     D = AllocIf (".ELSEIF", 0);
245                     NextTok ();
246
247                     /* Ignore the new condition if we are inside a false .ELSE
248                      * branch. This way we won't get any errors about undefined
249                      * symbols or similar...
250                      */
251                     if (IfCond == 0) {
252                         SetIfCond (D, ConstExpression ());
253                         ExpectSep ();
254                     }
255
256                     /* Get the new overall condition */
257                     IfCond = GetCurrentIfCond ();
258                 }
259                 break;
260
261             case TOK_ENDIF:
262                 /* We're done with this .IF.. - remove the descriptor(s) */
263                 FreeIf ();
264
265                 /* Be sure not to read the next token until the .IF stack
266                  * has been cleanup up, since we may be at end of file.
267                  */
268                 NextTok ();
269                 ExpectSep ();
270
271                 /* Get the new overall condition */
272                 IfCond = GetCurrentIfCond ();
273                 break;
274
275             case TOK_IF:
276                 D = AllocIf (".IF", 1);
277                 NextTok ();
278                 if (IfCond) {
279                     SetIfCond (D, ConstExpression ());
280                     ExpectSep ();
281                 }
282                 IfCond = GetCurrentIfCond ();
283                 break;
284
285             case TOK_IFBLANK:
286                 D = AllocIf (".IFBLANK", 1);
287                 NextTok ();
288                 if (IfCond) {
289                     if (TokIsSep (CurTok.Tok)) {
290                         SetIfCond (D, 1);
291                     } else {
292                         SetIfCond (D, 0);
293                         SkipUntilSep ();
294                     }
295                 }
296                 IfCond = GetCurrentIfCond ();
297                 break;
298
299             case TOK_IFCONST:
300                 D = AllocIf (".IFCONST", 1);
301                 NextTok ();
302                 if (IfCond) {
303                     ExprNode* Expr = Expression();
304                     SetIfCond (D, IsConstExpr (Expr, 0));
305                     FreeExpr (Expr);
306                     ExpectSep ();
307                 }
308                 IfCond = GetCurrentIfCond ();
309                 break;
310
311             case TOK_IFDEF:
312                 D = AllocIf (".IFDEF", 1);
313                 NextTok ();
314                 if (IfCond) {
315                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
316                     SetIfCond (D, Sym != 0 && SymIsDef (Sym));
317                 }
318                 IfCond = GetCurrentIfCond ();
319                 break;
320
321             case TOK_IFNBLANK:
322                 D = AllocIf (".IFNBLANK", 1);
323                 NextTok ();
324                 if (IfCond) {
325                     if (TokIsSep (CurTok.Tok)) {
326                         SetIfCond (D, 0);
327                     } else {
328                         SetIfCond (D, 1);
329                         SkipUntilSep ();
330                     }
331                 }
332                 IfCond = GetCurrentIfCond ();
333                 break;
334
335             case TOK_IFNCONST:
336                 D = AllocIf (".IFNCONST", 1);
337                 NextTok ();
338                 if (IfCond) {
339                     ExprNode* Expr = Expression();
340                     SetIfCond (D, !IsConstExpr (Expr, 0));
341                     FreeExpr (Expr);
342                     ExpectSep ();
343                 }
344                 IfCond = GetCurrentIfCond ();
345                 break;
346
347             case TOK_IFNDEF:
348                 D = AllocIf (".IFNDEF", 1);
349                 NextTok ();
350                 if (IfCond) {
351                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
352                     SetIfCond (D, Sym == 0 || !SymIsDef (Sym));
353                     ExpectSep ();
354                 }
355                 IfCond = GetCurrentIfCond ();
356                 break;
357
358             case TOK_IFNREF:
359                 D = AllocIf (".IFNREF", 1);
360                 NextTok ();
361                 if (IfCond) {
362                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
363                     SetIfCond (D, Sym == 0 || !SymIsRef (Sym));
364                     ExpectSep ();
365                 }
366                 IfCond = GetCurrentIfCond ();
367                 break;
368
369             case TOK_IFP02:
370                 D = AllocIf (".IFP02", 1);
371                 NextTok ();
372                 if (IfCond) {
373                     SetIfCond (D, GetCPU() == CPU_6502);
374                 }
375                 IfCond = GetCurrentIfCond ();
376                 ExpectSep ();
377                 break;
378
379             case TOK_IFP816:
380                 D = AllocIf (".IFP816", 1);
381                 NextTok ();
382                 if (IfCond) {
383                     SetIfCond (D, GetCPU() == CPU_65816);
384                 }
385                 IfCond = GetCurrentIfCond ();
386                 ExpectSep ();
387                 break;
388
389             case TOK_IFPC02:
390                 D = AllocIf (".IFPC02", 1);
391                 NextTok ();
392                 if (IfCond) {
393                     SetIfCond (D, GetCPU() == CPU_65C02);
394                 }
395                 IfCond = GetCurrentIfCond ();
396                 ExpectSep ();
397                 break;
398
399             case TOK_IFPSC02:
400                 D = AllocIf (".IFPSC02", 1);
401                 NextTok ();
402                 if (IfCond) {
403                     SetIfCond (D, GetCPU() == CPU_65SC02);
404                 }
405                 IfCond = GetCurrentIfCond ();
406                 ExpectSep ();
407                 break;
408
409             case TOK_IFREF:
410                 D = AllocIf (".IFREF", 1);
411                 NextTok ();
412                 if (IfCond) {
413                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
414                     SetIfCond (D, Sym != 0 && SymIsRef (Sym));
415                     ExpectSep ();
416                 }
417                 IfCond = GetCurrentIfCond ();
418                 break;
419
420             default:
421                 /* Skip tokens */
422                 NextTok ();
423
424         }
425
426     } while (IfCond == 0 && CurTok.Tok != TOK_EOF);
427 }
428
429
430
431 int CheckConditionals (void)
432 /* Check if the current token is one that starts a conditional directive, and
433  * call DoConditionals if so. Return true if a conditional directive was found,
434  * return false otherwise.
435  */
436 {
437     switch (CurTok.Tok) {
438         case TOK_ELSE:
439         case TOK_ELSEIF:
440         case TOK_ENDIF:
441         case TOK_IF:
442         case TOK_IFBLANK:
443         case TOK_IFCONST:
444         case TOK_IFDEF:
445         case TOK_IFNBLANK:
446         case TOK_IFNCONST:
447         case TOK_IFNDEF:
448         case TOK_IFNREF:
449         case TOK_IFP02:
450         case TOK_IFP816:
451         case TOK_IFPC02:
452         case TOK_IFPSC02:
453         case TOK_IFREF:
454             DoConditionals ();
455             return 1;
456
457         default:
458             return 0;
459     }
460 }
461
462
463
464 void CheckOpenIfs (void)
465 /* Called from the scanner before closing an input file. Will check for any
466  * open .ifs in this file.
467  */
468 {
469     const LineInfo* LI;
470
471     while (1) {
472         /* Get the current file number and check if the topmost entry on the
473          * .IF stack was inserted with this file number
474          */
475         IfDesc* D = GetCurrentIf ();
476         if (D == 0) {
477             /* There are no open .IFs */
478             break;
479         }
480
481         LI = CollConstAt (&D->LineInfos, 0);
482         if (LI->Pos.Name != CurTok.Pos.Name) {
483             /* The .if is from another file, bail out */
484             break;
485         }
486
487         /* Start of .if is in the file we're about to leave */
488         LIError (&D->LineInfos, "Conditional assembly branch was never closed");
489         FreeIf ();
490     }
491 }
492
493
494
495 unsigned GetIfStack (void)
496 /* Get the current .IF stack pointer */
497 {
498     return IfCount;
499 }
500
501
502
503 void CleanupIfStack (unsigned SP)
504 /* Cleanup the .IF stack, remove anything above the given stack pointer */
505 {
506     while (IfCount > SP) {
507         FreeIf ();
508     }
509 }
510
511
512
513